简体   繁体   中英

Default template paramters and partial specialization

I'm trying to understand partial template specialization with default arguments. If I remove the cast to void in the is_comparable specialization the value printed is always false, while if I keep the cast to (void) things work fine. Can someone please explain why the cast to void is needed? It likely has to do with matching the default template argument for T3, but I'm trying to get some additional insight here into the caveats with template partial specialization and default template parameters.

template<typename T1, typename T2, typename T3 = void>
struct is_comparable
{
 static const bool value = false;
};

template<typename T1, typename T2>
struct is_comparable<T1, T2, decltype((void)(std::declval<T1>() == std::declval<T2>()))>
{
  static const bool value = true;
};

int main()
{
   cout << boolalpha;
   cout << is_comparable<int, char>::value << endl;
   cout << is_comparable<int, float *>::value << endl;
}

Update: I observe the same behavior even when pointers are used. I would assume the partial specialization would be picked in this case?

template<typename T1, typename T2, typename T3 = void>
struct is_comparable
{
 static const bool value = false;
};

template<typename T1, typename T2>
struct is_comparable<T1*, T2*, decltype((std::declval<T1>() == std::declval<T2>()))>
{
  static const bool value = true;
};

int main()
{
   cout << boolalpha;
   cout << is_comparable<int*, int*>::value << endl;
}

The compiler instantiates the primary template and gets the following:

is_comparable<int, char, void>

Then, in order to select the best match, it tries to instantiate your specialization (assuming there is no typecast void and assuming the type substitution succeeds) and gets the following type:

is_comparable<int, char, bool>

It does not match the primary template at all, so it cannot be a better match.

We use primary template for default, so

  • is_comparable<int, char> is is_comparable<int, char, void>
  • is_comparable<int, float *> is is_comparable<int, float*, void>
  • is_comparable<int*, int*> is is_comparable<int*, int*, void>

Now, your specialization without void cast is mostly (unless special overload of operator== ) is

  • is_comparable<T1, T2, bool /* with SFINAE */>

So doesn't match expected parameters. You would have to use:

  • is_comparable<int, char, bool>
  • is_comparable<int, float *, bool>
  • is_comparable<int*, int*, bool>

Another way to cast to void is std::void_t :

template<typename T1, typename T2, typename Enabler = void>
struct is_comparable : std::false_type {};

template<typename T1, typename T2>
struct is_comparable<T1, T2,
                     std::void_t<decltype(std::declval<T1>() == std::declval<T2>())>>
    : std::true_type {};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM