SFINAEd-out函数是否会影响基类中显式导入的重载

Mor*_*enn 11 c++ sfinae shadowing language-lawyer c++11

在遇到另一个设计的问题之后,我决定make make一个包装类来向基类的某些成员函数添加重载,当且仅当基类中不存在可行的重载时.基本上,这是我想要做的:

template<typename T>
struct wrapper: T
{
    using T::foo;

    template<typename Arg>
    auto foo(Arg) const
        -> std::enable_if_t<not std::is_constructible<Arg>::value, bool>
    {
        return false;
    }
};

struct bar
{
    template<typename Arg>
    auto foo(Arg) const
        -> bool
    {
        return true;
    }
};
Run Code Online (Sandbox Code Playgroud)

在这个简单的例子中,只有当基类中的那个不可行时才wrapper添加一个重载foo(我简化std::enable_if为最简单的东西;原始的一个涉及检测习语).但是,g ++和clang ++不同意.请考虑以下事项main:

int main()
{
    assert(wrapper<bar>{}.foo(0));
}
Run Code Online (Sandbox Code Playgroud)

g ++没关系:foofrom wrapper<bar>是SFINAEd,所以它使用的是bar相反的.在另一方面,铛++似乎假定wrapper<bar>::foo 永远的阴影bar::foo,即使SFINAEd出来.这是错误消息:

main.cpp:30:26: error: no matching member function for call to 'foo'
    assert(wrapper<bar>{}.foo(0));
       ~~~~~~~~~~~~~~~^~~

/usr/include/assert.h:92:5: note: expanded from macro 'assert'
  ((expr)                                                               \
        ^

/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/type_traits:2388:44: note: candidate template ignored: disabled by 'enable_if' [with Arg = int]
    using enable_if_t = typename enable_if<_Cond, _Tp>::type;
                                               ^

1 error generated.
Run Code Online (Sandbox Code Playgroud)

那么,谁是对的?这个代码应该像clang ++一样被拒绝,还是应该工作和调用bar::foo

Col*_*mbo 7

考虑§10.2:

在声明集中,using-declaration被替换为派生类成员(7.3.3)隐藏或覆盖的指定成员集,

§7.3.3去了

using声明将基类中的名称带入派生类范围时,派生类中的成员函数模板覆盖和/或隐藏具有相同名称的成员函数和成员函数模板,parameter-type-list(8.3基类中的.5 [dcl.fct]),cv-qualification和ref-qualifier(如果有的话)(而不是冲突的).

显然,您的示例中唯一的区别在于返回类型.因此,Clang是正确的,并且GCC被窃听.

CWG#1764引入了措辞:

根据7.3.3 [namespace.udecl]第15段,

当using声明将基类中的名称带入派生类范围时,[...]

但是,10.2 [class.member.lookup]中给出的类范围名称查找算法没有实现此要求.没有任何东西可以从结果集中删除隐藏的基类成员(将第3段中的using声明替换).

该决议于2014年2月移至DR,因此GCC可能尚未实施.


正如在@ TartanLlama的回答中所提到的,你可以引入一个对应物来处理另一个案例.有点像

template <typename Arg, typename=std::enable_if_t<std::is_constructible<Arg>{}>>
decltype(auto) foo(Arg a) const
{
    return T::foo(a);
}
Run Code Online (Sandbox Code Playgroud)

演示.