【问题标题】:Comparison on std::tuple with multiple empty members doesn't compile on GCC在 GCC 上无法编译 std::tuple 与多个空成员的比较
【发布时间】:2019-12-02 20:28:21
【问题描述】:

当一个元组中有多个没有成员的结构时,尝试在 GCC 上编译元组的比较会导致以下错误:

<source>: In function 'bool foo()':
<source>:120:16: error: request for member 'operator<' is ambiguous
  120 |     return a < b;
      |                ^
<source>:46:10: note: candidates are: 'bool N::operator<(const N&) const'
   46 |     bool operator<(const N &) const noexcept {
      |          ^~~~~~~~
<source>:46:10: note:                 'bool N::operator<(const N&) const'
Compiler returned: 1

奇怪的是,当我将我在元组中使用的相同类型绑定在一起时,它会按预期工作。一个空结构也可以,不同类型的两个不行。

使用 clang 或 msvc 编译通过并产生预期结果。

这是正确的行为,还是 GCC/libstdc++ 错误?

演示

Try it,先取消注释所需的测试用例)

#include <tuple>

struct A {
    int value;
    A(int value) : value(value) {}

    bool operator==(const A &other) const noexcept {
        return value == other.value;
    }

    bool operator!=(const A &other) const noexcept {
        return value != other.value;
    }

    bool operator<(const A &other) const noexcept {
        return value < other.value;
    }
};

struct N {
    bool operator==(const N &) const noexcept {
        return true;
    }

    bool operator!=(const N &) const noexcept {
        return false;
    }

    bool operator<(const N &) const noexcept {
        return false;
    }
};

struct M {
    bool operator==(const M &) const noexcept {
        return true;
    }

    bool operator!=(const M &) const noexcept {
        return false;
    }

    bool operator<(const M &) const noexcept {
        return false;
    }
};



using AAKey = std::tuple<A, A>;
using ANAKey = std::tuple<A, N, A>;
using ANANKey = std::tuple<A, N, A, N>;
using ANAMKey = std::tuple<A, N, A, M>;
using NKey = std::tuple<N>;
using NNKey = std::tuple<N, N>;
using NMKey = std::tuple<N, M>;

bool foo() {
    /* Works
    AAKey a{0, 1};
    AAKey b{0, 0};
    //*/

    /* Works
    ANAKey a{0, N{}, 1};
    ANAKey b{0, N{}, 0};
    //*/

    /* Fails
    ANANKey a{0, N{}, 0, N{}};
    ANANKey b{0, N{}, 1, N{}};
    //*/

    /* Fails
    ANAMKey a{0, N{}, 0, M{}};
    ANAMKey b{0, N{}, 1, M{}};
    //*/


    /* Works
    NKey a{N{}};
    NKey b{N{}};
    //*/

    /* Fails
    NNKey a{N{}, N{}};
    NNKey b{N{}, N{}};
    //*/

    /* Fails
    NMKey a{N{}, M{}};
    NMKey b{N{}, M{}};
    //*/

    // Tying ANANKey into tuple:
    /* Works
    A ax1{0}, ay1{0}, ax2{0}, ay2{1};
    N nx1, ny1, nx2, ny2;
    auto a = std::tie(ax1, nx1, ax2, nx2);
    auto b = std::tie(ay1, ny1, ay2, ny2);
    //*/

    return a < b;
}

编辑

外部运算符重载确实有效(感谢@Turtlefight):

#include <tuple>

struct O {
    friend bool operator==(const O &, const O &) noexcept {
        return true;
    }

    friend bool operator!=(const O &, const O &) noexcept {
        return false;
    }

    friend bool operator<(const O &, const O &) noexcept {
        return false;
    }
};

using OOKey = std::tuple<O, O>;

bool foo() {
    OOKey a{O{}, O{}};
    OOKey b{O{}, O{}};

    return a < b;
}

【问题讨论】:

  • 我会说这两个候选者是相同的功能这一事实很明显这是一个错误..
  • @LightnessRacesinOrbit 此外,NMKey 案例给出了两个不同的候选函数:bool N::operator&lt;(const N&amp;) constbool M::operator&lt;(const M&amp;) const
  • clang 上似乎不会发生这种情况。添加single member int dummy; 或使用external operator&lt; overloads 似乎可以解决它。也许是编译器错误?
  • @Turtlefight 确实外部重载工作,这是我可以使用的解决方法,感谢您的注意。

标签: c++ g++ c++14


【解决方案1】:

这显然是一个错误,因为标准不允许比较不起作用。

原因更有趣。 libstdc++的元组是EBO的,如果N是一个空类,tuple&lt;N&gt;实际上是从N派生的。当您执行a &lt; b 时,我们需要对operator&lt; 进行非成员和成员查找。非成员查找按预期找到tupleoperator&lt;

但是,此成员查找在两个不同的基中找到 operator&lt;,因此通过 [class.member.lookup]p6-7,此查找会产生一个无效集合并且当场是不正确的。

Clang 似乎通过抑制成员查找中的错误来接受此代码,但我目前在标准中没有看到任何允许这种行为的内容。

【讨论】:

  • 很好的分析,谢谢。是否有任何其他方法可以规避查找,还是只有 clang 的解决方案/破解?
  • 作为图书馆用户,使用隐藏好友是最简单的解决方法。
猜你喜欢
  • 1970-01-01
  • 2021-03-17
  • 2020-05-18
  • 2013-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-04
相关资源
最近更新 更多