Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select constructor through SFINAE in template arguments

I'm trying to select a constructor through SFINAE as following:

template<typename T>
class MyClass
{
public:
    template<typename C, typename = std::enable_if_t<std::is_class<C>::value>>
    MyClass(C) { }

    template<typename C, typename = std::enable_if_t<std::is_pointer<C>::value>>
    MyClass(C) { }
};

but the compiler complains with following error:

error C2535: 'MyClass::MyClass(C)': member function already defined or declared

without even instantiating the constructor.

I worked out a working but ugly solution which i don't want to use because of the extra unused parameter:

template<typename T>
class MyWorkingClass
{
public:
    template<typename C>
    MyWorkingClass(C, std::enable_if_t<std::is_class<C>::value>* = nullptr) { }

    template<typename C>
    MyWorkingClass(C, std::enable_if_t<std::is_pointer<C>::value>* = nullptr) { }
};

A short usage example is given here:

void* ptr = nullptr;
MyClass<int> mc1(ptr);

std::vector<int> vec;
MyClass<int> mc2(vec);

// Shall raise an error
// MyClass<int> mc2(0);

The traits std::is_pointer and std::is_class are just an example, the original traits are more complicated.

Is there a way to select the constructor through SFINAE without adding another parameter to the constructor (maybe very close to the first appproach)?

like image 744
Denis Blank Avatar asked Aug 23 '15 13:08

Denis Blank


2 Answers

The problem is that the default values of arguments are not part of the template method signature. So you have two template<class C,class>ctor(c) identical ctors.

template<class T>
struct MyClass {
  template<class C,
    std::enable_if_t<std::is_class<C>{}>* =nullptr
  >
  MyClass(C) { }
  template<class C,
    std::enable_if_t<std::is_pointer<C>{}>* =nullptr
  >
  MyClass(C) { }
};

here we use template value arguments of dependant type. They never conflict, as the type of the pointer template argument is dependant on the type argument.

like image 147
Yakk - Adam Nevraumont Avatar answered Nov 14 '22 23:11

Yakk - Adam Nevraumont


Add one more (dummy) type template parameter:

template <typename C
        , typename = std::enable_if_t<std::is_class<C>::value>>
MyClass(C) { }

template <typename C
        , typename = std::enable_if_t<std::is_pointer<C>::value>
        , typename = void>
//        ~~~~~~~~~~~~~~^
MyClass(C) { }

DEMO

like image 42
Piotr Skotnicki Avatar answered Nov 14 '22 21:11

Piotr Skotnicki