Reputation: 555
I have the following code where I want a template class to check the template value as one of int64_t
or unint64_t
upon init. The class has a GetValue
method that is implemented outside the class.
template <typename T, typename std::enable_if<std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>>::type>
class Class
{
public:
Class(T value) noexcept :
cachedValue(value) {}
T GetValue() noexcept;
private:
T cachedValue;
};
template <typename T>
T Class<T>::GetValue() noexcept
{
// placeholder for now.
return cachedValue;
}
However, I am running into build errors C2955
, C2956
, C2976
or something similar from the enable_if
even after trying several ideas from other SO posts. How do I get this working?
Upvotes: 0
Views: 272
Reputation: 217075
Issue with
template <typename T,
typename std::enable_if<std::is_same_v<T, int64_t>
|| std::is_same_v<T, uint64_t>>::type>
class Class;
is that is result into
template <typename T_OK, void> class Class;
or
template <typename T_KO, /*Subsitution failure*/> class Class;
Which are both incorrect.
For non-friendly cases, (which might be enough most of the time), you might simply use static_assert
:
template <typename T> class Class
{
static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>);
/// ...
};
Else there are several variation (I will shorter the condition to cond_v<T>
)
Common one :( :
template <typename T, typename = std::enable_if_t<cond_v<T>>>
class Class
{
/// ...
};
Class<int64_t> ok;
Class<float> ko; // doesn't compile, as expected
// but might be hijacked:
Class<float, void> hijacked; // compile, unexpected
Fixed:
template <typename T, std::enable_if_t<cond_v<T>, int> = 0>
class Class
{
/// ...
};
Class<int64_t> ok;
Class<float> ko; // doesn't compile, as expected
One which allow customization
template <typename T, typename = void>
class Class;
template <typename T>
class Class<T, std::enable_if_t<cond_v<T>>>
{
/// ...
};
Class<int64_t> ok;
Class<float> ko; // incomplete type: doesn't compile, as expected
// user might add additional specialization:
template <typename T>
class Class<std::vector<T>, void> {/*..*/};
mostly useful to create customizable traits (as you specialize whole class).
C++20 introduces concept which shorter and simplify syntax:
restreint allowed type (there are several equivalent syntax)
template <typename T> requires(cond_v<T>)
class Class { /*..*/};
Allow customization, take best specialization:
template <typename T>
class Class;
// Specialization
template <typename T> requires(cond_v<T>)
class Class<T> { /*..*/};
Upvotes: 1
Reputation: 1015
there is also a traditional way to make class template SFINAE-friendly before C++20:
template<typename T, typename = void>
class Class; // <1> you can define it here if the conditions are not satisfied.
template<typename T>
class Class<T, typename std::enable_if<std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>>::type>{
public:
Class(T value) noexcept :cachedValue(value) {}
T GetValue() noexcept;
private:
T cachedValue;
};
int main(){
Class<int64_t> a = 0; // ok
Class<uint64_t> b = 0; // ok
Class<int32_t> c = 0; // incomplete-type. you can define it at <1>.
}
Upvotes: 0
Reputation: 7090
You can use @WhozCraing method, use a static_assert
to provide some message/instruction or use Concepts
with C++20
, as follows
template<class T>
concept ints64 = std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>;
template <typename T> requires(ints64<T>)
struct Class{
Class(T value) noexcept : cachedValue(value) {}
T GetValue() noexcept;
private:
T cachedValue;
};
template <typename T>requires(ints64<T>)
T Class<T>::GetValue() noexcept{
return cachedValue;
}
int main(){
return Class<int64_t>{0}.GetValue();//return Class<int>{0}.GetValue(); won't compile
}
Upvotes: 0