为std::string重载运算符+是个坏主意吗
overloading operator+ for std::strings - a bad idea?
现在通常应该稀疏地使用运算符重载,尤其是在涉及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); ^
https://godbolt.org/z/bLBssp
问题是找不到模板的operator+
定义。模板中的operator+
通过非限定名称查找进行解析。这基本上做了两件事:
在每个封闭作用域中递归查找任何
operator+
,直到找到第一个为止。如果在全局范围内或在不同的命名空间中为std::string
和std::wstring
定义自己的operator+
,则当有任何operator+
"更接近"模板时,就无法以这种方式找到它。查找与运算符(ADL(的参数类型相关联的命名空间。由于这两种类型都来自
namespace std
,因此我们在其中查找并没有发现任何有效的operator+
(请参阅godbolt上的其他错误注释(。你不能把你自己的运算符放在那里,因为这是未定义的行为。
因此,经验法则是:只重载涉及您的类型的运算符,因为该运算符必须与您的类型放在同一命名空间中,ADL才能工作。
即使没有模板,问题也是一样的,但在这种情况下,手动引入操作员可能是合理的。这显然是不合理的要求通用代码(甚至可能不是你的(
我担心您的用户可能没有意识到他们正在使用此功能。尽量避免像这样的隐式转换。
在需要的时候只写to_utf8
很容易。
如果您有string
和wstring
的大量混合,请在源代码处修复它:当您最初接收到宽字符串时,在string
中转换为UTF-8,那么所有"内部"字符串都很好且一致。
- cppcheck在const std::string[]上引发警告
- 将std::string传递给WriteConsole API
- 为std::string的某个索引赋值
- 使用 std::string () const 函数启动线程或未来
- 当我们进行一些操作时,应该使用什么'std::string'或'std::stringstream'?
- 如何更改大小(std::string)
- std::string 的对象真的可以移动吗?
- SegFault 同时使用 std::string::operator+= 和函数作为参数
- 无法从 std::string 中提取C++ Unicode 符号
- std::string 构造函数如何处理固定大小的 char[]?
- 真的没有来自 std::string_view 的 std::string 的显式构造函数吗?
- 将C++ std::string 转换为 UTF-16-LE 编码的字符串
- 重载 + 自己的类和 std::string 的运算符
- 如何使用 std::string 作为 QHash 的键?
- 将日语 wstring 转换为 std::string
- 可以从std::string继承以提供类型一致性吗
- 构造函数采用std::string_view与std::string并移动
- 在共享缓冲区内存中创建 ::std::string 对象
- std::string.size() 未知行为
- Valgrind 在 std::string::swap 中报告 SIGILL