【问题标题】:Why doesn't SFINAE work across multiple inheritance?为什么 SFINAE 不能跨多重继承工作?
【发布时间】:2019-12-04 14:19:20
【问题描述】:

代码如下:

#include <utility>
#include <type_traits>

template <class T>
class ClassWithDisabledFoo
{
public:
    template <class U = T, std::enable_if_t<std::is_integral<U>::value, int> = 0>
    void foo(){}
};

class ClassWithFoo
{
public:
    void foo(){}
};

class Derived: public ClassWithFoo, public ClassWithDisabledFoo<double>
{

};

void test()
{
    Derived d;
    d.foo();
}


在调用 d.foo() 时,clang 和 gcc 都表示对 foo 的调用是不明确的,尽管 ClassWithDisabledFoo::fooenable_if 禁用。如果我将foo 定义从ClassWithFoo 移动到Derived,代码就会编译。

为什么这不起作用,我怎样才能使它起作用?

【问题讨论】:

    标签: c++ templates sfinae


    【解决方案1】:

    您的示例中没有发生重载解决方案,因此 SFINAE 无关紧要。

    这里的关键是 名称查找 发生在 重载解析之前。名称查找找到一个重载集,然后对找到的集执行重载解析。

    名称必须映射到单个对象类型中的单个集合。在您的情况下,相同的名称映射到两个不同子对象的成员,因此名称查找是不明确的。一个直接的解决方案是添加 using 声明:

    class Derived: public ClassWithFoo, public ClassWithDisabledFoo<double>
    {
        using ClassWithFoo::foo;
        using ClassWithDisabledFoo::foo;
    };
    

    这会将名称引入Derived,现在可以明确地发现声明引用了由我们引入的成员组成的重载集。

    【讨论】:

    • 谢谢。可悲的是,这是有道理的。
    • 嗯。我想知道为什么如果所有可行的候选人都来自同一来源,他们会关心来自其他地方的不可行的候选人。它有什么(安全?)优势吗?
    • 额外问题:为什么这两个名字不能成为重载集的一部分?或者换句话说,为什么不能重载解析之后执行歧义检查?是否有特殊原因将其标准化,或者它只是我们必须忍受的那些随机 C++ 怪癖之一?
    • @user697683 - 基类可以更改。如果它引入了一个在重载解析中更匹配的函数,那么在当前方法中,事情不会编译。相反,如果我们无论如何都做了重载决议,那么新的重载就会被选中。因此代码会构建,但行为会有所不同,恕不另行通知。那会很糟糕。
    • @Deduplicator - 问题在于潜在的可行和更好的(隐式转换方式)重载潜入。
    猜你喜欢
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2015-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-05
    • 2011-03-25
    相关资源
    最近更新 更多