在类设计中查找外部命名空间中的重载运算符
Look up overloaded operators in outer namespace in class design
如何让命名空间中的包装类知道在外部/全局命名空间中,它包装的对象可能存在重载运算符?
注意:我听说过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
在阅读了元维基上的问题和答案,并遵循评论的建议后,我在自己的问题中移动了一些"更新",并将它们正式发布为自我答案。我这样做是希望这将帮助将来碰巧遇到相同问题的人。谢谢!
正如@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"
的顺序如何,用户代码都将进行编译。
- 从父命名空间重载类型
- 在 C++17 中的命名空间和子命名空间中重载运算符是不明确的
- Visual Studio 无法解决类和命名空间中重载的明确函数
- 在类设计中查找外部命名空间中的重载运算符
- 重载运算符 + 用于向量:命名空间标准
- 在命名空间中使用指令和函数重载
- 使用类指针重载C++命名空间函数模板专用化替代方法?
- 为什么找不到使用命名空间中定义的类型实例化的 std::weak_ptr 的重载运算符==?
- 在命名空间内的类中使用带有运算符重载的字符串流时"no match for ‘operator>>’"
- 在命名空间内的 lambda 中使用时未找到运算符重载
- 从类方法调用命名空间中名为 Same 的函数时,重载解析失败
- 为命名空间中的类重载运算符<<时发出警告
- 为在与类方法中的类相同的命名空间中定义的结构调用重载运算符
- 在全局命名空间中重载不依赖于用户定义类型的标准定义类型的运算符是否格式正确?
- 基于 SFINAE 的跨命名空间的运算符重载
- 如何在不同的命名空间中指定重载运算符
- 为什么使用范围解析运算符会更改调用全局命名空间中的哪个重载模板?
- C++无法重载类(和命名空间)中的函数
- C++:在我的命名空间中使用重载运算符
- 命名空间 + 重载 std::ostream <<运算符