现在通常应该稀疏地使用运算符重载 - 特别是当它涉及 stdlib 时。
虽然我很好奇可能存在哪些陷阱(如果有的话),但除了读者可能无法清楚地看到代码中发生的情况的明显陷阱之外,是否有任何技术理由避免这种>特定重载?
std::string operator+(const std::string& lhs, const std::wstring& rhs) {
return lhs + to_utf8(rhs);
}
(还有用于进行反向变换的双重载)
我发现这可以使一些操作更容易写出来,例如:
std::wstring{L"hel"} + "lo " + getName();
优点和缺点是什么,特别是您是否看到任何可能“适得其反”的场景(技术场景)?
性能不是问题。
请您参考如下方法:
您不应该这样做,因为它会破坏参数依赖查找 (ADL)。
考虑这个无辜的测试代码:
namespace myNamespace
{
struct MyType {};
MyType operator+(MyType, MyType);
template<class T>
auto doSomething(T t)
{
return t + std::wstring{};
}
}
看起来没有问题,不是吗?
嗯,是这样的:
void test()
{
std::string s;
myNamespace::doSomething(s);
}
error: invalid operands to binary expression ('std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >' and 'std::wstring' (aka 'basic_string<wchar_t>')) return t + std::wstring{}; ~ ^ ~~~~~~~~~~~~~~ <source>:25:18: note: in instantiation of function template specialization 'myNamespace::doSomething<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >' requested here myNamespace::doSomething(s); ^ <source>:12:12: note: candidate function not viable: no known conversion from 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >' to 'myNamespace::MyType' for 1st argument MyType operator+(MyType, MyType); ^
问题是找不到模板的 operator+
定义。模板中的operator+
通过非限定名称查找来解析。这基本上做了两件事:
在每个封闭范围中递归查找任何
operator+
,直到找到第一个。如果您在全局范围或不同的命名空间中为std::string
和std::wstring
定义自己的operator+
,则不能当任何operator+
“更接近”模板时发现这种方式。查看与运算符 (ADL) 参数类型关联的命名空间。由于这两种类型都来自
namespace std
,我们查看那里,发现没有可用的operator+
(请参阅 godbolt 上的其他错误注释)。您不能将自己的运算符放在那里,因为这是未定义的行为。
因此,经验法则:仅重载涉及您的类型的运算符,因为该运算符必须放置在与您的类型相同的命名空间中才能使 ADL 正常工作。
<小时 />问题是 the same即使没有模板,但在这种情况下, pull in the operator manually 可能是合理的。要求通用代码(甚至可能不是您的)显然是不合理的。