谁能解释一下为什么这个特性不能正常工作?

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

我试着写一个特征来检查 class 是否有静态函数,但它总是给我错误。谁能告诉我问题出在哪里?

#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;
}

以下代码有效:

#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;
}

这些例子之间似乎没有太大区别。

谁能告诉我第一个代码的问题在哪里?

C++17 注释,17.5.7.2 说,

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 ]

这是一步过程,后续模板参数替换从不适用于此。

让我们玩你的第一个例子:

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 {};

现在您可以看到:

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

有趣的是,由于 C++17、17.5.7.3

,您对 void_t 的想法可行

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

我建议尽可能使用 struct-based 而不是 alias-based 模板。否则,让它发挥作用就像点燃它一样困难 self-immolation