为std :: strings重载operator +-一个坏主意?

dar*_*une 2 c++ operator-overloading c++17

现在通常应该稀疏使用运算符重载-特别是在涉及stdlib时。

尽管我很好奇,除了读者可能无法清楚地看到代码中正在发生什么的显而易见的隐患之外,还有什么隐患?如果有技术上的原因,可以避免这种特殊的重载吗?

std::string operator+(const std::string& lhs, const std::wstring& rhs) {
    return lhs + to_utf8(rhs);
}
Run Code Online (Sandbox Code Playgroud)

(也有用于执行逆变换的双重载)

我发现这可以使某些操作更容易写出来,例如:

std::wstring{L"hel"} + "lo " + getName();
Run Code Online (Sandbox Code Playgroud)

优点和缺点是什么,特别是您看到任何可能“适得其反”的情况(技术上)吗?

性能不是问题。

Max*_*hof 5

您不应该这样做,因为它会破坏参数依赖查找(ADL)。

考虑以下无害的测试代码:

namespace myNamespace
{
    struct MyType {};

    MyType operator+(MyType, MyType);

    template<class T>
    auto doSomething(T t)
    {
        return t + std::wstring{};
    }
}
Run Code Online (Sandbox Code Playgroud)

看起来没有问题,对吗?

好吧,它是:

void test()
{
    std::string s;
    myNamespace::doSomething(s);
}
Run Code Online (Sandbox Code Playgroud)
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);
           ^
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/bLBssp

问题是operator+找不到模板的定义。该operator+模板通过不合格的名称查找解析。这基本上可以做两件事:

  1. 在每个封闭范围中递归查找任何对象 operator+,直到找到第一个。如果你定义自己operator+std::string,并std::wstring在全球范围内,或者在不同的命名空间,它不能找到这样的时候有任何 operator+ “接近”的模板。

  2. 查看与运算符(ADL)的参数类型关联的名称空间。由于这两种类型均来自namespace std,因此我们在其中查找并没有找到任何operator+有效的方法(请参阅Godbolt上的其他错误说明)。您不能在此处放置自己的运算符,因为这是未定义的行为。

因此,经验法则是:仅重载涉及您的类型的运算符,因为必须使该运算符与您的类型放在同一命名空间中,才能使ADL工作。


即使没有模板,问题也相同,但是在这种情况下,手动引入操作员可能是合理的。显然,要求通用代码(甚至可能不是您的代码)是不合理的。