重载命名空间中类模板的输出运算符
我这个程序
#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <algorithm>
using namespace std ;
#if 0
namespace skg
{
template <class T>
struct Triplet ;
}
template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t) ;
#endif
namespace skg
{
template <class T>
struct Triplet
{
// friend ostream& ::operator<< <> (ostream& os, const Triplet<T>& p_t) ;
private:
T x, y, z ;
public:
Triplet (const T& p_x, const T& p_y, const T& p_z)
: x(p_x), y(p_y), z(p_z) { }
} ;
}
template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)
{
os << '(' << p_t.x << ',' << p_t.y << ',' << p_t.z << ')' ;
return os ;
}
namespace {
void printVector()
{
typedef skg::Triplet<int> IntTriplet ;
vector< IntTriplet > vti ;
vti.push_back (IntTriplet (1, 2, 3)) ;
vti.push_back (IntTriplet (5, 5, 66)) ;
copy (vti.begin(), vti.end(), ostream_iterator<IntTriplet> (cout, "\n")) ;
}
}
int main (void)
{
printVector() ;
}
编译失败,因为编译器找不到 skg::Triplet 的任何输出运算符。但输出运算符确实存在。
如果我将 Triplet 从 skg 命名空间移动到全局命名空间,一切都会正常。这里出了什么问题?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您需要将
operator<<
的实现移至与您的类相同的命名空间中。它正在寻找:但由于参数依赖查找(ADL)的缺陷而找不到它。 ADL 意味着当您调用自由函数时,它会在其参数的命名空间中查找该函数。这与我们可以这样做的原因相同:
即使
operator<<(std::ostream&, const char*)
位于std
命名空间中。对于您的调用,这些命名空间是std
和skg
。它将在两者中查找,而不是在
skg
中找到一个(因为您的位于全局范围内),然后在std
中查找。它将看到可能性(所有正常的运算符<<
),但没有一个匹配。 由于运行的代码(ostream_iterator
中的代码)位于命名空间std
中,因此对全局命名空间的访问完全消失了。通过放置运算符在同一个命名空间中,ADL 可以工作。 Herb Sutter 在一篇文章中对此进行了讨论:“一个温和的建议:修复 ADL。”。 (PDF)。事实上,这是文章中的一个片段(展示了一个缺点):
与您的情况相同。
Sutter 和 & 出版的《C++ 编码标准》一书Alexandrescu 有一个有用的指南:
遵循它,你和 ADL 都会很高兴。我推荐这本书,即使你买不到,至少也可以阅读我上面链接的 PDF;它包含您应该需要的相关信息。
请注意,移动运算符后,您将需要您的friend指令(以便您可以访问私有变量):
还有ta-da!固定的。
You need to move your implementation of
operator<<
into the same namespace as your class. It's looking for:But won't find it because of a short-coming in argument-dependent look-up (ADL). ADL means that when you call a free function, it'll look for that function in the namespaces of it's arguments. This is the same reason we can do:
Even though
operator<<(std::ostream&, const char*)
is in thestd
namespace. For your call, those namespaces arestd
andskg
.It's going to look in both, not find one in
skg
(since yours is in the global scope), then look instd
. It will see possibilities (all the normaloperator<<
's), but none of those match. Because the code running (the code inostream_iterator
) is in the namespacestd
, access to the global namespace is completely gone.By placing your operator in the same namespace, ADL works. This is discussed in an article by Herb Sutter: "A Modest Proposal: Fixing ADL.". (PDF). In fact, here's a snippet from the article (demonstrating a shortcoming):
Same situation you have.
The book "C++ Coding Standards" by Sutter and & Alexandrescu has a useful guideline:
Follow it and you and ADL will be happy. I recommend this book, and even if you can't get one at least read the PDF I linked above; it contains the relevant information you should need.
Note that after you move the operator, you'll need your friend directive (so you can access private variables):
And ta-da! Fixed.