Reputation: 4070
I'm trying to do SFINAE on a constructor. I want to enable one overload for integers and one for everything else. I know I can just make a base(int)
and base(T)
constructor but I want to do it this way instead.
template <class T>
struct base
{
template <class T1 = T>
base(T1, typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr) {}
template <class T1 = T>
base(T1, typename std::enable_if<!std::is_same<T1, int>::value>::type* = nullptr) {}
};
Then I make a main Base
class that inherits the constructors:
template <class T>
struct Base : base<T>
{
using base<T>::base;
};
But when I instantiate Base
with any T
I get these errors:
source_file.cpp:21:15: error: call to deleted constructor of 'Base<int>'
Base<int> v(4);
^ ~
source_file.cpp:16:25: note: deleted constructor was inherited here
using base<T>::base;
^
source_file.cpp:7:5: note: constructor cannot be inherited
base(T1, typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr) {}
^
When I instantiate base
directly it works without a problem. Why can't the constructor be inherited when I am doing SFINAE? Without the second constructor overload everything works fine.
Upvotes: 1
Views: 164
Reputation: 137880
Unlike the parameter list of a constructor, the entire template parameter list is always inherited including default template arguments. Put the SFINAE there:
template <class T>
struct base
{
template <class T1 = T,
typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr>
base(T1) {}
template <class T1 = T,
typename std::enable_if<!std::is_same<T1, int>::value>::type* = nullptr>
base(T1) {}
};
This works in Clang and GCC, in -std=c++11
and -std=c++1z
modes. (Only recent versions tested, 3.6 and 5.1, respectively.)
Upvotes: 0
Reputation: 42574
You could avoid this problem altogether in the example program by defining only the generic constructor in base
and providing a specialization for base<int>
(DEMO):
template <class T>
struct base
{
base(T) { std::cout << "generic constructor\n"; }
};
template <>
base<int>::base(int) { std::cout << "int specialization\n"; }
or use tag dispatching instead of SFINAE (DEMO):
template <class T>
class base
{
base(std::true_type, int) { std::cout << "int specialization\n"; }
base(std::false_type, T) { std::cout << "generic constructor\n"; }
public:
base(T t) : base(std::is_same<int, T>{}, std::move(t)) {}
};
Upvotes: 2