4

I am writing something about Partial template specialization.What I want to do is that using a template is_valid to check if the class has a member type named valid. The source code is as below.

#include <iostream>

class A {
public:
    typedef void valid;
};

class B {
public:
    typedef int valid;
};

class C {
};

template <typename T, typename U = void> struct is_valid
{
    const static bool value = false;
};

template <typename T> struct is_valid<T, typename T::valid>
{
    const static bool value = true;
};

int main()
{
    std::cout << is_valid<A>::value << std::endl;
    std::cout << is_valid<B>::value << std::endl;
    std::cout << is_valid<C>::value << std::endl;

    return 0;
}

However, the output is

1
0
0

The last 0 is clear because C class has no member. But why is in B case, the output is 0?

Can anyone help me to understand what is the mechanism of Partial template specialization here?

irasin
  • 145
  • 6

2 Answers2

4

For is_valid<B>, the primary template is found, since the 2nd template argument is not specified, the default value void is used, the instantiation is supposed to be is_valid<B, void>. Then specializations get checked. The problem is B::valid is of type int, the instantiation got from specialization would be is_valid<B, int>, which doesn't match the instantiation from the primary template; so the partial specialization won't be selected, the primary template is selected instead.

For is_valid<A>, same as above, the instantiation got from primary template is is_valid<A, void>. Since A::valid is of type void, the partial specialization gives instantiation as is_valid<A, void> too. The specialization is preferred and selected.

If you specify the 2nd template argument as int like is_valid<B, int>, the partial specialization will be selected.

If you just want to check whether the member type valid exists or not, you can use std::void_t which always yielding type void in the partial specialization.

template <typename T> struct is_valid<T, std::void_t<typename T::valid>> 
{
    const static bool value = true;
};

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Sorry, to make my question clear, I want to know, in which case the second template would be selected. Thanks a lot. – irasin Aug 14 '21 at 02:02
  • @irasin Specialization is preferred over primary template; that's why specialization exists. More precisely, if all are usable, explicit specialization is preferred over partial specialization, which is perferred over primary template. – songyuanyao Aug 14 '21 at 02:46
  • The problem is B::valid is of type int, the instantiation got from specialization would be is_valid, which doesn't match the instantiation from the primary template; Did you mean that only if the case of `is_valid` can match the primary template? I think `template struct is_valid` can match any type of `is_valid`. It seems there is something wrong here. – irasin Aug 14 '21 at 03:39
  • 1
    @irasin But you didn't specify the 2nd template argument, just write `is_vaid`, then the default value `void` is appied as `is_valid`, then the 2nd template parameter must be `void` when checking specializations. As I said, if you write it as `is_valid`, the partial specialization will be selected. – songyuanyao Aug 14 '21 at 03:44
1
template<typename _T>
struct have_member_valid
{
    template<typename T> static std::true_type check(decltype(&T::valid)*);
    template<typename T> static std::false_type check(...);

public:
    constexpr static bool value = decltype(check<_T>(0))::value;
};

Here is another example to tell whether a class member exists, but I don't quite sure how it works. Maybe param (...) has the lowest priority to match?

Refer: Templated check for the existence of a class member function? (modified a little)

wwc
  • 101
  • 6