可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Please refer to the code below:
#include <algorithm>
namespace N
{
template <typename T>
class C
{
public:
void SwapWith(C & c)
{
using namespace std; // (1)
//using std::swap; // (2)
swap(a, c.a);
}
private:
int a;
};
template <typename T>
void swap(C<T> & c1, C<T> & c2)
{
c1.SwapWith(c2);
}
}
namespace std
{
template<typename T> void swap(N::C<T> & c1, N::C<T> & c2)
{
c1.SwapWith(c2);
}
}
As written above, the code doesn't compile on Visual Studio 2008/2010. The error is:
'void N::swap(N::C<T> &,N::C<T> &)' : could not deduce template argument for 'N::C<T> &' from 'int'.
However, if I comment out (1) and uncomment (2), it will compile OK. What is the difference between using namespace std
and using std::swap
that explains this behavior?
回答1:
The first case is a using directive (using namespace X
), and what it means is that the names from namespace X
will be available for regular lookup, in the first common namespace of X
and the current scope. In this case, the first common namespace ancestor of ::N
and ::std
is ::
, so the using directive will make std::swap
available only if lookup hits ::
.
The problem here is that when lookup starts it will look inside the function, then inside the class, then inside N
and it will find ::N::swap
there. Since a potential overload is detected, regular lookup does not continue to the outer namespace ::
. Because ::N::swap
is a function the compiler will do ADL (Argument dependent lookup), but the set of associated namespaces for fundamental types is empty, so that won't bring any other overload. At this point lookup completes, and overload resolution starts. It will try to match the current (single) overload with the call and it will fail to find a way of converting from int
to the argument ::N::C
and you get the error.
On the other hand a using declaration (using std::swap
) provides the declaration of the entity in the current context (in this case inside the function itself). Lookup will find std::swap
immediately and stop regular lookup with ::std::swap
and will use it.
回答2:
The obvious reason is that a using declaration and a using
directive have different effects. A using declaration
introduces the name immediately into the current scope, so
using std::swap
introduces the name into the local scope;
lookup stops here, and the only symbol you find is std::swap
.
Also, this takes place when the template is defined, so later
declarations in namespace std
aren't found. In the following
line, the only swap
that will be considered is the one
defined in <algorithm>
, plus those added by ADL (thus, the one
in namespace N
). (But is this true with VC++? The compiler
doesn't implement name lookup correctly, so who knows.)
A using directive specifies that the names will appear "as if"
they were declared in the nearest namespace enclosing both the
directive and the nominated namespace; in your case, global
namespace. And it doesn't actually introduce the names; it
simply affects name lookup. Which in the case of a dependent
symbol (or always, in the case of VC++) takes place at the call
site.
As for why you have this particular error message: probably more
an issue with VC++, since there's certainly no non-deduceable
contexts in your code. But there's no reason to expect the two
variants be have the same behavior, regardless of the compiler.
回答3:
Note: I have removed your swap definition in namespace std. It is not relevant here. Even with out it the code will have the same issues.
This is due to look up rule differences between using directive
(using namespace std
) and the using
declaration
(using std::swap
)
Microsoft says
If a local variable
has the same name as a namespace variable, the namespace variable is
hidden. It is an error to have a namespace variable with the same name
as a global variable.
#include<iostream>
namespace T {
void flunk(int) { std::cout << "T";}
}
namespace V {
void flunk(int) { std::cout << "V";}
}
int main() {
using T::flunk; // makes T::flunk local
// using V::flunk; // makes V::flunk local. This will be an error
using namespace V; // V::flunk will be hidden
flunk(1);
}
According to this, due to your
template <typename T>
void swap(C<T> & c1, C<T> & c2)
std::swap
will be hidden when you use
using namespace std;
So the only swap
available for template deduction is N::swap
and it won't work for int
s because it expects a template class
as argument.
but not when
using std::swap;
In this case it becomes equivalent to local definition. And can be used with out problem.