2

Given the following class template:

#include <type_traits>

template< class T, class Unrelated >
struct MyClass
{
    static_assert( std::is_same< T, char >::value ||
                   std::is_same< T, char16_t>::value ||
                   std::is_same< T, char32_t >::value,
                   "MyClass only defined for char, char16_t, char32_t" );
    MyClass( T init ) {}
    MyClass( char32_t init ) {}
    // ...
};

The second (char32_t) constructor is a special case for T == char and T == char16_t.

Obviously, it would generate an error for T == char32_t. So, I would like to "knock out" that constructor for that case. The class is rather large, with most of the code being shared for all T, so I would rather not specialize the whole class for the char32_t case.

I have seen enable_if as well as related answers like this one here on SO, but was unable to adapt any of the presented solutions / examples to my specific case (non-templated constructor in class template).

So I ask for your assistance:

How to disable the MyClass( char32_t ) constructor for MyClass< T, U > with T == char32_t?

Alternatively, if that is easier, how to disable the MyClass( T init ) constructor for T == char32_t? (For char32_t, the two constructors are functionally identical.)

Community
  • 1
  • 1
DevSolar
  • 67,862
  • 21
  • 134
  • 209

2 Answers2

4

I have seen enable_if as well as related answers like this one here on SO, but was unable to adapt any of the presented solutions / examples to my specific case (non-templated constructor in class template).

You have to make your constructor templated, using a default argument. Then make sure that enable_if depends on the default argument. Shortly:

//void_t trick made a type dependant
template<class T, class ... >
struct always
{
    typedef T type;
};

template<class T, class ... D>
using always_t = typename always<T, D...>::type;

template<class T, class Unreleated>
class MyClass
{
public:
    // ... 
    MyClass( T init ) 
    {
    }

    template<class U = void>
    MyClass( std::enable_if_t<
                !std::is_same<T, char32_t>::value,
                always_t<char32_t, U> //< enable_if expression depends on U
             > init)
   {
      //decltype(init) is always char32_t, but the compiler can not know that
   }
};

Try it live.

sbabbi
  • 11,070
  • 2
  • 29
  • 57
  • This is still technically ill-formed NDR if instantiated with a non-char32_t. No valid specialization can be generated for the constructor template (you aren't allowed to specialize enable_if, so enable_if::type is always invalid). – T.C. Jan 30 '16 at 17:26
  • @T.C. Would swapping `always_t` and`char32_t` in my code solve the issue? – sbabbi Jan 30 '16 at 19:35
  • Sure, or `class U = T`, and then always use `U` and not `T`. – T.C. Jan 31 '16 at 12:00
0

This isn't quite the solution you've asked for, but it might solve the underlying problem.

You could implement your core functionality in Base<T, Unrelated> and then define:

template <typename T, typename U> struct MyClass : Base<T, U> {
    using Base::Base;
};

template <typename U> struct MyClass<char32_t, U> : Base<char32_t, U> {
    using Base::Base;

    MyClass(char32_t init) { /* your constructor here */ }
};
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084