为什么decltype(declval <T>().func())在decltype(&T :: func)不起作用的情况下工作?

jtb*_*des 9 c++ member-functions sfinae template-specialization c++11

我试图baz()在模板参数中检测成员函数的存在:

template<typename T, typename = void>
struct ImplementsBaz : public std::false_type { };

template<typename T>
struct ImplementsBaz<T, decltype(&T::baz)> : public std::true_type { };
Run Code Online (Sandbox Code Playgroud)

但它总是产生错误:

struct Foo {};
struct Bar { void baz() {} };

std::cout << ImplementsBaz<Foo>::value << std::endl;  // 0
std::cout << ImplementsBaz<Bar>::value << std::endl;  // also 0
Run Code Online (Sandbox Code Playgroud)

但是,使用declval和调用该方法确实有效:

template<typename T>
struct ImplementsBaz<T, decltype(std::declval<T>().baz())> : public std::true_type { };
Run Code Online (Sandbox Code Playgroud)

当然,现在这只能检测baz带有0个参数的函数.为什么在使用时正确选择了专业化declval<T>().baz(),但没有decltype(&T::baz)?

Tri*_*dle 6

如果您使用void_t“检测习惯用法”,那么它确实按预期工作:

template <typename...> using void_t = void;

template <typename T>
struct ImplementsBaz<T, void_t<decltype(&T::baz)>> : std::true_type {};


struct Bar { void baz() {} };

static_assert(ImplementsBaz<Bar>::value); // passes
Run Code Online (Sandbox Code Playgroud)

Godbolt 链接

至于为什么,这个问题详细解释了“void_t技巧”是如何工作的。引用已接受的答案:

就好像你写过一样has_member<A, void>::value。现在,将模板参数列表与模板的任何特化进行比较has_member。仅当没有专业化匹配时,主模板的定义才用作后备。

在原始情况下,decltype(&T::baz)is not void,因此特化与原始模板不匹配,因此不考虑。我们需要使用void_t(或某种其他机制,例如强制转换)将类型更改为,void以便使用特化。