SFINAE does not work for copy constructors

Viewed 144

I have a template class and I would like to have two copy ctors. One for trivial types, and another for non trivial types. The following code works (with one copy 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

When I try to do the same with two copy ctors, then it seems none of them was generated (both was SFINAEd away)

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;
    }
};

The code does not compile:

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
         ^~~~~~~~

I tried to google for it, and I think SFINAE should work with copy constructors as well. Please do not suggest using if constexpt or requires concept, this question is about SFINAE. Thanks for any help!

1 Answers

Unfortunately, a template constructor can't be a default, copy or move constructor.

The signature of a copy constructor for MyStruct is as follows

MyStruct (MyStruct const &);

This function, in your second example, is implicitly deleted because you've written an explicit move constructor.

Your template constructor, with the following signature,

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

is (are) a valid constructor but, given is a template one, the (unfortunately deleted) real copy constructor is preferred.

Possible solution: add a real copy constructor that call (delegating constructor) the template constructor, through an additional argument.

I mean

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; }
};
Related