在此代码中
struct A {int commmon; int rare;};
struct B {int commmon;};
struct L {
template<class T>
int f(const T& t) {return t.commmon;}
template<class T>
int f(T& t) {return t.rare;}
};
void func() {
A a; B b; L l;
l.f(a);
l.f(B{});
l.f(b);
}
最后几行给我错误
In instantiation of ‘int L::f(T&) [with T = B]’:
error: ‘struct B’ has no member named ‘rare’
但是,根据我对SFINAE的理解,由于体内的替换失败,第二次过载应该忽略。为什么这没有发生?
编辑::如果我将第二个重载的返回类型更改为decltype(T::rare)
,它将完成我想要的操作。那么我的SF需要碰巧是NAE吗?
答案 0 :(得分:2)
SFINAE不适用于功能主体[temp.deduct/8]:
在函数类型,其模板参数类型及其显式说明符的直接上下文中,只有无效的类型和表达式才可能导致推论失败。
答案 1 :(得分:2)
要解决的最繁琐的方法是在约束更重的情况下使用auto
返回类型和尾随返回类型:
struct L {
template <class T>
auto f(const T& t) {return t.commmon;}
template <class T>
auto f(T& t) -> decltype(t.rare) {return t.rare;}
};
此方法的优点是,在编译器已经看到函数参数的位置指定约束,从而允许使用比模板参数声明中的std::enable_if
子句更短的符号:
#include <type_traits>
struct L {
template <class T>
int f(const T& t) {return t.commmon;}
template <class T, std::enable_if_t<std::is_same_v<std::decay_t<T>, A>, int> = 0>
int f(T& t) { return t.rare;}
};
请进一步注意,传递右值参数时,不会调用更受约束的函数。您可能需要通过将功能签名更改为
来解决此问题template<class T /*, ... */>
int f(T&& t) { /* ... */ }
答案 2 :(得分:0)
模板参数。在评估实现时,扣除已经发生。因此,所有与推导相关的都是函数 signature ,从现有函数中可以选择non-const变体。
如果要根据现有成员应用SFINAE,则必须在函数签名中执行此操作。 lubgr的answer很好地反映了这一点:返回类型为decltype(t.rare)
;如果T不提供rare
成员,则无法推导返回类型,因此在解析过程中不考虑重载。
找到了与您可能感兴趣的问题有关的其他两个答案:C++11,compatible to pre-C++11