在具有相同名称的成员函数的情况下的ADL

Seb*_*ach 5 c++ interface argument-dependent-lookup non-member-functions

情况是某些成员函数bar::Bar::frobnicate想要利用ADL从具有相同名称的函数内的某个未知命名空间中查找函数.但是,它只找到自己的名字.

测试用例

(请注意,实际上,这Bar是一个Foo不可知的模板;这只是可重现的,最小的测试用例)

namespace foo {
    struct Foo {};
    void frobnicate(Foo const &) {}
}

namespace bar {
    struct Bar {
        void frobnicate() {
            foo::Foo foo;
            frobnicate(foo); // <-- error
        }
    };
}

int main () {
    bar::Bar x;
    x.frobnicate();
    frobnicate(foo::Foo());
}
Run Code Online (Sandbox Code Playgroud)

结果是:

test.cc: In member function ‘void bar::Bar::frobnicate()’:
test.cc:10:31: error: no matching function for call to ‘bar::Bar::frobnicate(foo::Foo&)’
test.cc:10:31: note: candidate is:
test.cc:8:18: note: void bar::Bar::frobnicate()
test.cc:8:18: note:   candidate expects 0 arguments, 1 provided
Run Code Online (Sandbox Code Playgroud)

标准

我理解这是正确的编译器行为:

3.4.1非限定名称查找[basic.lookup.unqual]

(...)一旦找到名称的声明(...),名称查找就会结束

并且只有非限定查找失败后,依赖于参数的查找才会起作用:

3.4.2依赖于参数的名称查找[basic.lookup.argdep]

当函数调用(5.2.2)中的postfix-expression是非限定id时,可以搜索在通常的非限定查找(3.4.1)期间未考虑的其他名称空间

解决方法

我目前的解决方法是引入一个特殊的traits类,它没有定义冲突名称本身:

    struct BarTraits {
        void frobnicate_(foo::Foo const &b) {
            frobnicate(b);
        }
    };
Run Code Online (Sandbox Code Playgroud)

或者这个版本:

    void frobnicate_(foo::Foo const &c) { frobnicate(c); }
Run Code Online (Sandbox Code Playgroud)

有没有比引入这些特质类更好的选择?

明确地将调用限定foo::frobnicate(foo)为不是一个选项,因为(如上所述)Bar该类Foo在实际中是一个模板,不仅应该用于foo命名空间中的类型.

Tem*_*Rex 2

正如您自己发现的那样,将成员函数添加frobnicate到 的类接口Bar(或Bar<T>在模板情况下)将阻止 ADL 查找foo::frobnicate.

向类(或类模板)添加功能的最简单(在本例中是惯用的)方法是将非成员函数 (或函数模板)添加到命名空间frobnicateBarBar<T>frobnicate(Bar)frobnicate(Bar<T>)bar

namespace foo {
    struct Foo {};
    void frobnicate(Foo const &)  {}
}

namespace bar {
    template<class T>
    struct Bar {    
       T t;    
    }; 

    template<class T>
    void frobnicate(Bar<T> const& b)
    {
        frobnicate(b.t);    
    }
}

int main () {
    bar::Bar<foo::Foo> x;
    frobnicate(x);
    frobnicate(foo::Foo());
}
Run Code Online (Sandbox Code Playgroud)

如果您坚持拥有成员函数,则必须将其重命名为类似do_frobnicate(). 我不会使用类型特征技巧来获得相同的行为,因为它是一种间接方法,并且使类接口更难以理解(记住 Stroustrup 的座右铭:“直接在代码中表达你的想法”)。