How to conditionally instantiate a template class which has more than one template parameter?
Asked Answered
M

1

7

I have followed this post: Class template SFINAE to instantiate the template class conditionally.

That works perfectly for the classes which have only one template parameters, as shown in the link above.

However, I have two (template)arguments, and I would like to do certain SFINE check. Following is a minimal example of my code.

#include <type_traits>
#include <string>

template<class T, class U, class R> using arithmetic_types =  std::enable_if_t<
    std::is_arithmetic_v<T> &&
    std::is_arithmetic_v<U>,
    R
>;

template<class T, class U, class Enable = void> class MyClass;
template<class T, class U, arithmetic_types<T, U, void>> 
class MyClass {
public:
    MyClass() = default;
};

int main()
{
    MyClass<int, int> o;          // should work
    MyClass<int, double> o1;      // should work
    MyClass<int, std::string> o2; // should be a complier error
    return 0;
}

Above gave me the error message: https://godbolt.org/z/BEWJMp

error C3855: 'MyClass': template parameter 'Enable' is incompatible with the declaration
error C2079: 'o' uses undefined class 'MyClass'
error C2079: 'o1' uses undefined class 'MyClass'
error C2079: 'o2' uses undefined class 'MyClass'

Unfortunately, I could not understand the error message(error C3855:).

Why I can't do the same principle shown in the above link to more template parameters?

And what is the best solution for this?

Marrin answered 3/5, 2019 at 7:49 Comment(3)
@Wolf I am using C++17 in MSVC 16.0Marrin
@PiotrSkotnicki Don't use SFINAE unless you have a usable alternative Could you reason the statement? any SO post?Marrin
SFINAE is basically the metaprogramming technique with the worst compile-time performance by far. It's not necessarily something you have to care about, but for large projects (or template libraries) this can be a point of contention eventually.Dougherty
V
6

The problem is in the template specialization of MyClass. The specialization should be parameterized only on the two classes T and U, the test should be put in the declaration, as in the example below.

#include <string>
#include <type_traits>

template <class T, class U, class R>
using arithmetic_types = std::enable_if_t<
    std::is_arithmetic_v<T> && std::is_arithmetic_v<U>, R>;

template <class T, class U, class Enable = void>
class MyClass;

template <class T, class U> //<- Remove the test from here
class MyClass<T, U, arithmetic_types<T, U, void>> //<- Put the test here.
{
public:
  MyClass() = default;
};

int main()
{
  MyClass<int, int> o;          // should work
  MyClass<int, double> o1;      // should work
  MyClass<int, std::string> o2; // should be a complier error
  return 0;
}

Demo: https://godbolt.org/z/xTnwo9

Vail answered 3/5, 2019 at 8:8 Comment(4)
It was hard to see (believe) the difference. +1 thanks to YSCAbohm
No need to pessimize std::is_arithmetic_v. It works fine even in msvc.Paramorph
@Paramorph Sorry, the compiler I go to when investigating this kind of problems has only a c++14 library.Vail
@Vail Oh, yeah... Now I see the mistake. Thanks for the answer.Marrin

© 2022 - 2024 — McMap. All rights reserved.