我正在尝试为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();
但事实并非如此,我得到了:
调用重载'Test()'是不明确的
使这个SFINAE工作的这些论点是什么?
答案 0 :(得分:3)
你的第二个例子无法编译,因为Test
有两个具有相同签名的重载,因为默认的模板类型参数不是函数签名的一部分。这是不允许的。
您的第一个示例以关注方式运作:
如果类型R
中包含函数test
,则Test
都会成为有效的重载候选项。但是,省略号函数的排名低于非省略函数,因此编译器选择第一个重载,返回true_type
。
当R上没有test
时,第一个重载将从重载分辨率集(工作中的SFINAE)中排除。您只剩下第二个,返回false_type
。
答案 1 :(得分:1)
这个问题已得到解答,但对于深入解释可能有用。
希望这个带注释的程序能让事情变得更清晰:
#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;
}