Reputation: 5342
I am have a custom class that I want to provide a templated version of the sin
function to - I've called it foo
in my minimum verifiable reporduction. Due to some template meta programming elsewhere, it's advantageous for my sin
function to have the same name as the normal sin
function as it is doing the same job, just on different types.
However, the following will not compile:
#include <cmath>
#include <iostream>
#include <cstdlib>
namespace test
{
template <std::size_t size>
class foo
{
};
template <std::size_t size>
void sin(const foo<size>& f)
{
}
template <class T>
void bar(const T& x)
{
using namespace ::std;
sin(x);
}
}
int main()
{
test::bar(0.5);
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
I get the following compiler error:
..\test\problem.cpp(23,3): error: no matching function for call to 'sin'
sin(x);
^~~
..\test\problem.cpp(29,8): note: in instantiation of function template specialization 'test::bar<double>' requested here
test::bar(0.5);
^
..\test\problem.cpp(14,7): note: candidate template ignored: could not match 'foo<size>' against 'const double'
void sin(const foo<size>& f)
^
1 error generated.
I am surprised by this. Is there a way to get bar
to compile without using template meta programming hacks to create several versions of bar
?
Fails in Clang 9 and MSVC 2019.
Upvotes: 2
Views: 129
Reputation: 15941
There is a difference between a using directive and a using declaration. using namespace ::std;
is a using directive. As far as unqualified name lookup is concerned, a using directive basically just forwards name lookup to also search namespace ::std
if a name was not found at the point name lookup reaches the nearest namespace that encloses both, the namespace that contained the directive and the namespace nominated by the directive [namespace.udir]/2. In your case, this means name lookup would have to reach the global namespace before it could find ::std::sin
. However, name lookup will reach the namespace test
first, which contains a function named sin
, and then just stop because it has found what it was looking for [basic.lookup.unqual]/1. What you want is a using declaration:
template <class T>
void bar(const T& x)
{
using std::sin;
sin(x);
}
Contrary to a using directive, a using declaration directly introduces the declared name into the scope in which the using declaration resides [namespace.udecl]/1, which means std::sin
will be found right away. Now, this means normal unqualified name lookup will not find test::sin
from within the block scope after the using declaration anymore. However, thanks to argument-dependent name lookup [basic.lookup.argdep], test::sin
will be found and part of the overload set again in case T
is an instance of test::foo
…
Upvotes: 3
Reputation: 51845
Here's a 'quick fix' based on (a) the comments and (b) the answer linked in the comments. A minimal version of your bar
function would be:
void bar()
{
::sin(0.5); // Without "::" your templated "test::sin" hides the global namespace
// version - which is here referenced EXPLICITLY.
}
Upvotes: 2