Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using enable_if to disable a template constructor of a template class

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;
};
like image 436
John Wayne Avatar asked Oct 20 '17 00:10

John Wayne


People also ask

How do I restrict a template type in C++?

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.

Can template class have constructor?

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 is the difference between template class and template Typename?

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).


2 Answers

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

like image 119
songyuanyao Avatar answered Nov 12 '22 16:11

songyuanyao


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
like image 1
kay Avatar answered Nov 12 '22 18:11

kay