I'd like to provide a hidden friend for my type, and at the same time there is another object with the same name in an inline namespace. Everything works fine with normal types.
But if I have a template, the compiler errors when I instantiate the template
redefinition of 'cpo' as different kind of symbol
Here is the code:
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
}
The smaller example is 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{};
}
btw, I am aware of other techniques such as tag_invoke
but here I have no control of the names and the library
Your code is well-formed. This is 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."
No timeline for a fix however.
As a workaround, you can wrap Foo2
in its own inline namespace
inline namespace disambig_detail {
template <typename T>
struct Foo2{
friend auto cpo(Foo2){
return 6;
}
};
}
This should alter the declarative region enough to avoid the mis-diagnosis. Meanwhile name lookup should proceed pretty much the same as if the namespace wasn't there.