I'm trying to disable a template constructor of a template class using std::enable_if
when the type of the arguments of the template constructor matches the type "MyClass
" so that I can use my other constructor which allows me to initialize the class of current template with the class of another one.
template <typename t, size_t size>
class MyClass
{
public:
MyClass() { data.fill(static_cast<T>(0)); }
template <typename... Args> // i want to disable this if Args = MyClass
MyClass(Args&&... args) : data{ std::forward<Args>(args)... } {}
template <size_t size2>
MyClass(const Myclass<t, size2>& other_sized_template) { /*do stuff*/ } // this won't work unless the template ctor above is disabled if the arguments passed are of type Myclass
private:
std::array<t, size> data;
};
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
This is basically a default constructor for the basic types. We can use this technique in our template code to create default constructors. For any generic type, T, we can explicitly call its default constructor. Our Measurement template can now be constructed with any type that has a default constructor.
what's the difference between template <typename T> and template <class T>? Nothing actually. typename is a designated keyword for this purpose but class is supported for backwards compatibility and historical reasons (before typename keyword was added).
You can check whether the type is an instantiation of MyClass
by a class template with partial specialization. e.g.
template<class...>
struct is_MyClass : std::false_type {};
template <typename T, size_t size>
struct is_MyClass<MyClass<T, size>> : std::true_type {};
then disable the constructor like
template <typename... Args,
typename = std::enable_if_t<
!is_MyClass<
std::remove_cv_t<
std::remove_reference_t<Args>>...>::value> > // i want to disable this if Args = MyClass
MyClass(Args&&... args) : data{ std::forward<Args>(args)... } {}
LIVE
First you need a helper to be able to tell if Args...
are a single MyClass<T, N>
:
#include <cstddef>
#include <type_traits>
template <class, size_t>
class MyClass;
template <class T>
struct IsMyClass {
template <size_t Size>
static std::true_type test(const MyClass<T, Size>&);
static std::false_type test(...);
template <class V, class... Further>
static constexpr bool value = (
(sizeof...(Further) == 0) &&
decltype(test(std::declval<V>()))::value
);
};
You can use it like
IsMyClass<int>::template value<Args...>
to test if Args...
are MyClass<int, Some_Int>
. Now use this helper:
#include <cstdio>
template <typename T, size_t Size>
class MyClass
{
public:
MyClass() {
printf("Default constructor\n");
}
template <
class ...Args,
class Check = std::enable_if_t<(!IsMyClass<T>::template value<Args...>)>
>
MyClass(Args&&... args) {
printf("Args != MyClass\n");
}
template <size_t Size2>
MyClass(const MyClass<T, Size2>& other_sized_template) {
printf("other_sized_template\n");
}
};
A simple test if it works:
int main() {
printf("init\n");
MyClass<int, 10> myclass_int10;
MyClass<void, 10> myclass_void10;
printf("1+2\n");
MyClass<int, 20> test1(myclass_int10);
MyClass<int, 20> test2(myclass_void10);
printf("3+4\n");
MyClass<void, 20> test3(myclass_int10);
MyClass<void, 20> test4(myclass_void10);
printf("5+6\n");
MyClass<int, 20> test5(myclass_int10, myclass_int10);
MyClass<int, 20> test6(myclass_void10, myclass_void10);
return 0;
}
And it does:
$ g++ -std=c++14 ./x.cpp
$ ./a.out
init
Default constructor
Default constructor
1+2
other_sized_template
Args != MyClass
3+4
Args != MyClass
other_sized_template
5+6
Args != MyClass
Args != MyClass
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With