【问题标题】:SFINAE does not work for copy constructorsSFINAE 不适用于复制构造函数
【发布时间】:2021-03-21 21:26:15
【问题描述】:

我有一个模板类,我想要两个复制 ctor。一个用于平凡类型,另一个用于非平凡类型。 以下代码有效(使用一个副本 ctor):

template <typename T>
struct  MyStruct
{
    MyStruct()
    {}
    
    MyStruct(const MyStruct& o)
    {
        std::cout << "copy ";
        foo(o);
    }
    
    template <typename U = T, typename std::enable_if_t<!std::is_trivial<U>::value, int> =0>
    void foo(const MyStruct& o)
    {
        std::cout << "Non trivial" << std::endl;
    }
    
    template <typename U = T, typename std::enable_if_t<std::is_trivial<U>::value, int> =0>
    void foo(const MyStruct& o)
    {
        std::cout << "Trivial" << std::endl;
    }
    
    MyStruct(MyStruct&& o)
    {
        std::cout << "Move" << std::endl;
    }
};

struct MyType
{
    MyType(int i){}
};

int main()
{
    MyStruct<int> my1;
    MyStruct<int> my2(my1);
    
    MyStruct<MyType> mytype1;
    MyStruct<MyType> mytype2(mytype1);
}

// prints
// Copy Trivial
// Copy Non trivial

当我尝试对两个复制 ctor 执行相同操作时,似乎它们都没有生成(都被 SFINAE 删除了)

struct  MyStruct
{
    MyStruct()
    {}
    
    template <typename U = T, typename std::enable_if_t<!std::is_trivial<U>::value, int> =0>
    MyStruct(const MyStruct& o)
    {
        std::cout << "Non trivial" << std::endl;
    }
    
    template <typename U = T, typename std::enable_if_t<std::is_trivial<U>::value, int> = 0>
    MyStruct(const MyStruct& o)
    {
        std::cout << "Trivial" << std::endl;
    }

    MyStruct(MyStruct&& o)
    {
        std::cout << "Move" << std::endl;
    }
};

代码无法编译:

main.cpp: In function ‘int main()’:
main.cpp:36:26: error: use of deleted function ‘constexpr MyStruct::MyStruct(const MyStruct&)’
     MyStruct<int> my2(my1);
                          ^
main.cpp:5:9: note: ‘constexpr MyStruct::MyStruct(const MyStruct&)’ is implicitly declared as deleted because ‘MyStruct’ declares a move constructor or move assignment operator
 struct  MyStruct
         ^~~~~~~~
main.cpp:39:37: error: use of deleted function ‘constexpr MyStruct::MyStruct(const MyStruct&)’
     MyStruct<MyType> mytype2(mytype1);
                                     ^
main.cpp:5:9: note: ‘constexpr MyStruct::MyStruct(const MyStruct&)’ is implicitly declared as deleted because ‘MyStruct’ declares a move constructor or move assignment operator
 struct  MyStruct
         ^~~~~~~~

我尝试用谷歌搜索它,我认为 SFINAE 也应该与复制构造函数一起使用。 请不要建议使用if constexpt requires concept,这个问题是关于SFINAE的。 感谢您的帮助!

【问题讨论】:

  • 模板化构造函数永远不是复制构造函数。除了您的两个模板之外,还有一个隐式声明的复制构造函数MyStruct(const MyStruct&amp; o) = delete;。在这种情况下,它被隐式声明为已删除,因为您的类提供了用户定义的移动构造函数。但它仍然在重载决议中胜过模板化构造函数。
  • 谢谢!您和 max66 给出了相似且有用的答案。

标签: c++ constructor sfinae


【解决方案1】:

很遗憾,模板构造函数不能是默认、复制或移动构造函数。

MyStruct的拷贝构造函数签名如下

MyStruct (MyStruct const &);

在您的第二个示例中,该函数被隐式删除,因为您编写了显式移动构造函数。

您的模板构造函数,具有以下签名,

template <typename, int>
MyStruct (MyStruct const &);

是(是)一个有效的构造函数,但是,给定一个模板,(不幸地删除)真正的复制构造函数是首选。

可能的解决方案:添加一个真正的复制构造函数,通过附加参数调用(委托构造函数)模板构造函数。

我是说

template <typename T>
struct  MyStruct
{
    MyStruct()
    {}

    // delegating real copy constructor added
    MyStruct(const MyStruct & ms0) : MyStruct{ms0, 0}
     { }
    
    template <typename U = T,
              std::enable_if_t<!std::is_trivial<U>::value, int> = 0>
    MyStruct(const MyStruct& o, int = 0) // <-- added an int argument
     { std::cout << "Non trivial" << std::endl; }
    
    template <typename U = T,
              std::enable_if_t<std::is_trivial<U>::value, int> = 0>
    MyStruct(const MyStruct& o, int = 0) // <-- added and int argument
     { std::cout << "Trivial" << std::endl; }

    MyStruct(MyStruct&& o)
     { std::cout << "Move" << std::endl; }
};

【讨论】:

  • 感谢您的解释,以及可能的解决方案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多