There was a question somewhat related to my problem earlier, which dealt with template class, that used std::enable_if
on a method, which is declared in class prototype, but the actual implementation is done outside.
Source: function implementation with enable_if outside of class definition
I want to do something similar, but with the class constructor, which I want to define outside template class with std::enable_if
metafunction.
template <typename T>
using EnableIfArithmetic = typename std::enable_if<std::is_arithmetic<T>::value, void>::type;
template <typename NumericType>
class SomeClass {
public:
// constructor definition
template <typename = EnableIfArithmetic<NumericType>>
SomeClass() {
// do some stuff
}
};
Desired form:
template <typename NumericType>
class SomeClass {
public:
// constructor declaration
template <typename = EnableIfArithmetic<NumericType>>
SomeClass();
};
// constructor definition
template <typename NumericType>
template <typename = EnableIfArithmetic<NumericType>>
SomeClass<NumericType>::SomeClass() {
// constructor implementation
}
But I cannot get it right, without compiling error. What am I doing wrong?
Default values for the template arguments should not be repeated in the definitions. For example:
template<typename N>
struct S {
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
S(T);
};
template<typename N>
template<typename T, typename>
S<N>::S(T) { }
The way you use SFINAE is not correct: EnableIfArithmetic
should depend on some deduced type (in the same template). Please refer to this question. For example:
template<typename T = N, typename = EnableIfArithmetic<T>>
S() { }
Otherwise, a hard fail will occur:
error: no type named 'type' in 'struct std::enable_if'
If you want to disable the default constructor for some types N
, you can also use static_assert
inside the constructor. However, it won't be SFINAE friendly.
template<typename N>
struct S1 {
public:
template<typename T = N, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
S1() { }
};
template<typename N>
struct S2 {
public:
S2() { static_assert(std::is_arithmetic_v<N>); }
};
static_assert(std::is_default_constructible_v<S1<int>>);
static_assert(!std::is_default_constructible_v<S1<void>>);
static_assert(std::is_default_constructible_v<S2<int>>);
static_assert(std::is_default_constructible_v<S2<void>>);