SFINAE为什么不选择const-reference-take重载?

时间:2018-07-17 07:46:08

标签: c++ c++14

在此代码中

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吗?

3 个答案:

答案 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,则必须在函数签名中执行此操作。 lubgranswer很好地反映了这一点:返回类型为decltype(t.rare);如果T不提供rare成员,则无法推导返回类型,因此在解析过程中不考虑重载。

找到了与您可能感兴趣的问题有关的其他两个答案:C++11compatible to pre-C++11