【问题标题】:Why doesn't SFINAE select the const-reference-taking overload? [duplicate]为什么 SFINAE 不选择 const-reference-taking 重载? [复制]
【发布时间】:2018-07-17 07:46:08
【问题描述】:

在这段代码中

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的理解,由于body中的替换失败,第二次重载应该被忽略。为什么不会发生这种情况?

编辑:如果我将第二个重载的返回类型更改为decltype(T::rare),它会做我想要的。那么我的 SF 在哪里需要恰好是 NAE?

【问题讨论】:

  • 在这里阅读解释:en.cppreference.com/w/cpp/language/sfinae 简而言之:SF 必须在函数声明中,而不是在主体中。
  • @mch 哇,这个解释比维基百科的解释好多了。
  • 您可以使用类似template&lt;class T&gt; auto f(T&amp; t) -&gt; decltype(t.rare) {return t.rare;} 的方式为您的函数启用 SFINAE。

标签: c++ c++14


【解决方案1】:

解决这个问题的最简洁的方法是在更多约束重载上使用带有尾随返回类型的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】:

    SFINAE 不适用于函数体[temp.deduct/8]

    只有在函数类型、它的模板参数类型和它的显式说明符的直接上下文中的无效类型和表达式会导致推导失败。

    【讨论】:

      【解决方案3】:

      模板参数在函数实际实例化之前推导出来。在评估实施时,扣除已经发生。所以与演绎相关的只有函数 signature,从现有的函数中,非常量变体可以选择。

      如果您想根据现有成员应用获取 SFINAE,您必须已经在函数签名中执行此操作。 lubgranswer 很好地反映了这一点:返回类型是decltype(t.rare);如果 T 没有提供rare 成员,则无法推断返回类型,因此在解析期间不考虑重载。

      找到了另外两个关于您可能感兴趣的问题的答案:C++11compatible to pre-C++11

      【讨论】:

        猜你喜欢
        • 2019-07-23
        • 2011-05-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多