【问题标题】:How to select correct operator overload如何选择正确的运算符重载
【发布时间】:2020-01-03 18:22:04
【问题描述】:

我在namespace a 中定义了这些数据和运算符:

namespace a {
enum class E {
    VALUE
};

std::ostream& operator<<(std::ostream& os, const E e)
{
    return os << "VALUE";
}
}

我的代码在namespace b 中运行,它想使用另一个operator&lt;&lt; 来表示相同的类型,替换原来在namespace a 中定义的那个:

namespace b {

std::ostream& operator<<(std::ostream& os, const a::E e)
{
    return os << "value";
}

void f()
{
    const auto e = a::E::VALUE;
    std::cout << e << std::endl;
}
}

int main()
{
    b::f();
    return 0;
}

由于 ADL,编译器会感到困惑,并将对 operator&lt;&lt; 的调用视为模棱两可的调用,因为 namespace anamespace b 版本都可用。

当用户代码在 namespace b 内时,我怎样才能让它使用我的 operator&lt;&lt; 版本而没有歧义?

【问题讨论】:

  • 不能帮助自己,但要注意不是编译器会感到困惑,而是它做了“正确的事情”,它不是你所期望的;)。它必须是operator&lt;&lt;,还是命名函数也可以?
  • @foreknownas_463035818 with function 更容易指定要使用的函数的哪个重载,这就是为什么我将问题放在运算符重载方面,即使问题是相同的:即使使用函数,我不想每次都指定使用哪个重载,我只想指定一次。
  • 真正的答案是只有命名空间a 有“权利”为a::E 类型定义规范operator&lt;&lt;。命名空间b 中的operator&lt;&lt; 应更改为命名函数。

标签: c++ namespaces operator-overloading argument-dependent-lookup


【解决方案1】:

另一种方式:

namespace b {

template<class T>
struct Wrapper { T value; };

template<class T>
inline Wrapper<T> wrap(T t) {
    return {t};
}

std::ostream& operator<<(std::ostream& os, Wrapper<a::E> e) {
    return os << "value";
}

void f() {
    const auto e = a::E::VALUE;
    std::cout << wrap(e) << std::endl;
}

} // namespace b

或者,使用可重复使用的Wrapper

template<class Tag, class T>
struct Wrapper { T value; };

template<class Tag, class T>
inline Wrapper<Tag, T> wrap(T t) {
    return Wrapper<Tag, T>{t};
}

namespace b {

struct Tag {};

std::ostream& operator<<(std::ostream& os, Wrapper<Tag, a::E> e) {
    return os << "value";
}

void f() {
    const auto e = a::E::VALUE;
    std::cout << wrap<Tag>(e) << std::endl;
}

} // namespace b

【讨论】:

  • wrap() 每次都需要写。有没有办法只指定一次我想在namespace b 中始终使用operator&lt;&lt;namespace b 版本?
  • @nyarlathotep108 为您添加了可重复使用的Wrapper 版本。
  • 例如,我尝试在操作符调用前添加using b::operator&lt;&lt;;,但没有解决歧义。
  • 在 C++17 中,函数 wrap 基本上是不需要的,Wrapper{e}/Wrapper{Tag{}, e} 就足够了。
  • 您的包装器确实复制了顺便说一句。 (对于枚举没有问题,但可能对于自定义类)。
【解决方案2】:

为了消除歧义,您可以完全限定调用:

b::operator &lt;&lt;(std::cout, e) &lt;&lt; std::endl;

不过,我同意运营商并不是很好。

【讨论】:

    猜你喜欢
    • 2013-02-17
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    相关资源
    最近更新 更多