简体   繁体   中英

Could anyone explain me why this trait does not work properly?

I tried to write a trait to check if a class has static function, but it always gives me the false. Could anyone tell me where is the problem?

#include <iostream>

template <template <typename...> class Trait, typename Ret, typename T>
struct is_detected : std::false_type 
{
//This helps me to check that Get_t<A> is int
static_assert(std::is_same<Trait<T>, Ret>::value, "");
};

template <template <typename...> class Trait, typename T>
struct is_detected<Trait, Trait<T>, T> : std::true_type {};

class A {
public:
  static int Get() {
    std::cout << "I'm in get\n";
    return 0;
  }
};

template <typename T>
using Get_t = decltype(T::Get());

//template <typename T>
//using supports_Get = is_detected<Get_t, int, T>;

int main() {
  std::cout << is_detected<Get_t, int, A>::value << std::endl;
  return 0;
}

The following code works:

#include <iostream>
#include <type_traits>

namespace detail {
template <template <typename...> class Trait, typename V, typename T>
struct is_detected : std::false_type {};

template <template <typename...> class Trait, typename T>
struct is_detected<Trait, std::void_t<Trait<T>>, T> : std::true_type {};
}

class A {
public:
  static int Get() {
    std::cout << "I'm in get\n";
    return 0;
  }
};

template <typename T>
using Get_t = decltype(T::Get());

int main() {
  std::cout << detail::is_detected<Get_t, void, A>::value << std::endl;
  return 0;
}

Seems like there is not big difference between these examples.

Could anyone make me understand where is the problem of first code?

Note to C++17, 17.5.7.2 says, that

When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template. [ Note: An alias template name is never deduced. — end note ]

This is one step process and subsequent template argument substitution never applies here.

Lets play with your first example:

template <template <typename> class Trait, typename Ret, typename T>
struct is_detected {};

struct A {
  static int Get() {
    return 0;
  }
};

template <typename T>
using Get_t = decltype(T::Get());

template <typename T>
struct Get_t2 {
  using Type = decltype(T::Get());
};

template <template <typename> class Trait, typename T>
struct is_detected<Trait, typename Trait<T>::Type, T> : std::true_type {};

template <template <typename> class Trait, typename T>
struct is_detected<Trait, Trait<T>, T> : std::true_type {};

Now you can see, that:

is_detected<Get_t2, int, A>::value // works, because it is normal type
is_detected<Get_t, int, A>::value // not, because it is alias

Interestingly your idea with void_t works because of C++17, 17.5.7.3

However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id

I recommend to use struct-based not alias-based templates whenever possible. Otherwise to make it work will be as hard as to kindle its self-immolation

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