为什么这些模板化函数不能不带参数?

Why Can't These Templatized Functions Take No Arguments?

我正在尝试为 Substitution Fail Is Not An Error(SFINAE) 使用几个模板化函数。我可以这样做:

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test(R*);
template<typename R> static false_type Test(...);

但我不明白这个论点是如何让这个 SNFIAE 发挥作用的。似乎我应该能够删除参数并且模板选择将以完全相同的方式工作:

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test();
template<typename R> static false_type Test();

但是没有,我得到:

Call of overloaded 'Test()' is ambiguous

使这个 SFINAE 起作用的这些论点是什么?

您的第二个示例无法编译,因为 Test 的两个重载具有相同的签名,因为默认模板类型参数不是函数签名的一部分。这是不允许的。

您的第一个示例按以下方式运行:

当类型 R 确实有函数 test 时,两个 Test 都成为有效的重载候选者。但是,省略号函数的等级低于非省略号函数,因此编译器选择第一个重载,返回 true_type.

当 R 上没有 test 时,第一个重载被排除在重载决议集之外(SFINAE 在工作)。你只剩下第二个,returns false_type.

问题已得到解答,但进行更深入的解释可能会有用。

希望这个带注释的程序能让事情变得更清楚:

#include <utility>
#include <iostream>

// define the template function Test<R> if and only if the expression
// std::declval<R>().test()
// is a valid expression.
// in which case Test<R, decltype(std::declval<R>().test())>(0) will be preferrable to... (see after)
template<typename R, typename S = decltype(std::declval<R>().test())> 
  static std::true_type Test(R*);

// ...the template function Test<R>(...)
// because any function overload with specific arguments is preferred to this
template<typename R> static std::false_type Test(...);


struct foo
{
  void test();
};

struct bar
{
  // no test() method
//  void test();
};


// has_test<T> deduces the type that would have been returned from Test<T ... with possibly defaulted args here>(0)
// The actual Test<T>(0) will be the best candidate available
// For foo, it's Test<foo, decltype(std::declval<R>().test())>(foo*)
// which deduces to
// Test<foo, void>(foo*)
// because that's a better match than Test<foo>(...)
//
// for bar it's Test<bar>(...)
// because Test<bar, /*error deducing type*/>(bar*)
// is discarded as a candidate, due to SFNAE
//
template<class T>
constexpr bool has_test = decltype(Test<T>(0))::value;


int main()
{
  std::cout << has_test<foo> << std::endl;
  std::cout << has_test<bar> << std::endl;
}