Reputation: 10315
I am working on a library that makes extensive use of C++ templates. While writing I came to situation with code like this (of course simplified):
#include <sstream>
namespace ns{
template<class A, class B>
class c{};
class a{};
template<class A>
class b{
public:
template<class B>
auto
take(A, B){
return c<A, B>{};
}
};
template<class A>
auto
make_b(A){
return b<A>{};
}
template<class A, class B>
auto
operator|(A _a, B _b){
return _b.take(_a, _b);
}
}
using namespace ns;
int main(){
a{} | make_b(a{});
return 0;
}
While compiling it with msvc 19 (Visual Studio 2017) I get following class of errors:
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/xlocale(314): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc (28): error C2228: left of '.take' must have class/struct/union (28): note: type is ''
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/xstring(2923): note: see reference to function template instantiation 'auto ns::operator |(A,B)' being compiled with [ A=unsigned int, B= ] /opt/compiler-explorer/windows/19.10.25017/lib/native/include/xstring(2922): note: while compiling class template member function 'void std::basic_string,std::allocator>::shrink_to_fit(void)'
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/system_error(658): note: see reference to function template instantiation 'void std::basic_string,std::allocator>::shrink_to_fit(void)' being compiled
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/stdexcept(22): note: see reference to class template instantiation 'std::basic_string,std::allocator>' being compiled
Removing using namespace
works, but I do not want to forbid it (why should I?). Is there a solution?
EDIT: Of course I tested code with GCC and Clang - compiles under GCC from 4.9 and clang3 so it is solely MSVC issue.
EDIT2: I took a look at the reported errors and it seems like MSVC is extending the scope of overloaded operator|
inside its standard library when using using namespace
.
Upvotes: 2
Views: 344
Reputation: 19093
It works this way, but i can't explain why it doesn't work original way, hope it will help somebody else.
Though my guess is that your template is too broad and it gets intantiated by some code from std library. Actually your code also works if your just comment out any std headers or remove using namespace ns
.
namespace ns {
template<class A, class B>
class c {};
class a {};
template<class A>
class b {
public:
using MyA = A;
template<class B>
auto
take(A, B) {
return c<A, B>{};
}
};
template<class A>
auto
make_b(A) {
return b<A>{};
}
template<class B>
auto
operator|(typename B::MyA _a, B _b) {
return _b.take(_a, _b);
}
}
Upvotes: 1