class 模板名称中的隐藏好友与内联命名空间中的另一个符号冲突

hidden friend in a class template name clashes with another symbol in inline namespace

我想为我的类型提供一个隐藏的好友,同时在内联命名空间中还有另一个同名对象。对于普通类型一切正常。

但是如果我有模板,当我实例化模板时编译器会出错

redefinition of 'cpo' as different kind of symbol

代码如下:

namespace lib{

    struct CPO{
        template<typename... T>
        constexpr decltype(auto) operator()(T&&... args) const{
            cpo(static_cast<T&&>(args)...);
        }
    };

    inline namespace cpo_impl_{
        inline constexpr CPO cpo{};
    }

    struct Foo1{
        friend auto cpo(Foo1){
            return 5;
        }
    };

    template <typename T>
    struct Foo2{
        friend auto cpo(Foo2){
            return 6;
        }
    };
}

int main(){
    lib::Foo1 f1{};
    lib::cpo(f1); // works fine;

    lib::Foo2<int> f2{}; // error here
}

较小的例子在这里

namespace lib{

    inline namespace impl_{
        inline constexpr int foo = 5;
    }

    template <typename T>
    struct Bar{
        friend auto foo(Bar){
            return 4;
        }
    };
}

int main(){
    lib::bar<int> b{};
}

顺便说一句,我知道其他技术,例如 tag_invoke 但在这里我无法控制名称和库

您的密码是 well-formed。这是Clang Bug 37556.

Compiling this well-formed program with -std=c++2a:

namespace X {
    inline namespace Y { int swap; }

    template<class>
    struct S {
        friend void swap(S&, S&) {}
    };
}

int main() {
    X::S<int> s1, s2;
    swap(s1, s2);
}

produces diagnostics (https://godbolt.org/g/ceWLxY):

<source>:6:21: error: redefinition of 'swap' as different kind of symbol
        friend void swap(S&, S&) {}
                    ^
<source>:11:15: note: in instantiation of template class 'X::S<int>' requested here
    X::S<int> s1, s2;
            ^
<source>:2:30: note: previous definition is here
    inline namespace Y { int swap; }
                            ^
1 error generated.

Note that the program compiles successfully if S is replaced by a non-template class. Discussion on the CWG reflector verified that this program is well-formed, including Richard's statement "Oops, Clang's redeclaration check in the template instantiation case is incorrectly performing a redeclaration lookup as if for a qualified name here, rather than a redeclaration lookup for an unqualified name."

但是没有修复的时间表。


作为解决方法,您可以将 Foo2 包装在它自己的 inline namespace

inline namespace disambig_detail {
  template <typename T>
  struct Foo2{
    friend auto cpo(Foo2){
        return 6;
    }
  };
}

这应该足以改变声明区域以避免 mis-diagnosis。同时,名称查找的过程应该与命名空间不存在时的过程几乎相同。