如何让命名空间中的包装类知道在外部/全局命名空间中可能存在它包装的对象的重载操作符?
注意:我听说过ADL或Koenig查找,但我遇到了一个真正的问题。
真正的设计问题
我想设计一个仅限标题的库。说我把所有东西都放在命名空间my
中。与此问题相关的部分可以简化为模板包装器item
。
// my.hpp
#include <iostream>
namespace my
{
template<typename T>
struct item
{
T thing;
item(T t) : thing(t) {}
};
template<typename T>
std::ostream & operator<<(std::ostream & os, const item<T> & it)
{
os << it.thing;
return os;
}
}
使用item
我想要实现的是:
item<T>
包裹一个T
对象(T
对象由用户提供)operator<<(std::ostream &, const T &)
中没有定义<iostream>
,那么我认为用户已经超载了operator<<(std::ostream &, const T &)
,我想要operator<<(std::ostream &, const item<T> &)
来调用它。具体的用户示例
考虑一组用于T = std::vector<double>
的用户代码
// user.hpp
// #include guard omitted
#include <iostream>
#include <vector>
std::ostream & operator<<(std::ostream &, const std::vector<double> &);
和
// user.cpp
#include <iostream>
#include <vector>
std::ostream & operator<<(std::ostream & os, const std::vector<double> & v)
{
for (const auto & e : v)
os << e << " | ";
return os;
}
int main()
{
std::vector<double> vec = {3.14, 2.83};
std::cout << my::item<std::vector<double>>(vec);
}
现在如果用户放了
#include "user.hpp"
#include "my.hpp"
在user.cpp
的开头,一切都会好的,g++ user.cpp
会按预期编译。
但是,如果用户更改了订单并放入
#include "my.hpp"
#include "user.hpp"
编译器会生成一个错误说
my.hpp: In function 'std::ostream& my::operator<<(std::ostream&, const my::item<T>&)':
my.hpp:15:23: error: '::operator<<' has not been declared
当然我不希望结果依赖于#include
的顺序。
我的问题是:作为名称空间my
和包装item<T>
的设计者,我能对my
和item<T>
做什么,以便item<T>
可以正确发现并调用operator<<(std::ostream, const T &)
,如果它是由用户提供的?
感谢您的时间!
更新:为了您的信息,g++ --version
返回
g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0
在阅读了关于meta的question and answer之后,并根据评论的建议,我正在我自己的问题中移动一些“更新”并正式发布它们作为自我回答。我这样做是希望这将有助于将来碰巧遇到同样问题的人。谢谢!
正如@xskxzr在评论中正确指出的那样,用户代码中有一些不好的东西。再具体一点,
声明参数都是
std
实体的函数/操作是不好的,因为你不能将这些声明添加到std中以利用ADL
在这种情况下,问题出在用户一边,而不是设计者身上。
现在,如果用户进行了更改
// user.hpp
#include <iostream>
#include <vector>
// CHANGE: (privately) inherit from std::vector<double>, rather than overload directly
struct DoubleVector : private std::vector<double>
{
using std::vector<double>::vector;
friend
std::ostream & operator<<(std::ostream &, const DoubleVector &);
};
std::ostream & operator<<(std::ostream &, const DoubleVector &);
和
// user.cpp
#include "my.hpp"
#include "user.hpp"
#include <iostream>
#include <vector>
// CHANGE: use a user-defined DoubleVector class
std::ostream & operator<<(std::ostream & os, const DoubleVector & c)
{
for (const auto & e : c)
os << e << " | ";
return os;
}
int main()
{
DoubleVector vec = {3.14, 2.83};
std::cout << my::item<DoubleVector>(vec);
}
然后,无论#include "my.hpp"
和#include "user.hpp"
的顺序如何,用户代码都将编译。