简体   繁体   English

std :: tuple与多个空成员的比较不能在GCC上编译

[英]Comparison on std::tuple with multiple empty members doesn't compile on GCC

When there are multiple structures with no members in a tuple, trying to compile a comparison of tuples on GCC results in following error: 当元组中没有成员的多个结构时,尝试编译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

Strangely enough, when I tie the same types I use in the tuple, it works as expected. 奇怪的是,当我绑定我在元组中使用的相同类型时,它按预期工作。 One empty structure is also OK, two with different types are not. 一个空结构也可以,两个不同类型的结构也没有。

Compilation with clang or msvc passes and yields expected results. 使用clang或msvc进行编译并传递预期结果。

Is this correct behaviour, or is it a GCC/libstdc++ bug? 这是正确的行为,还是GCC / libstdc ++错误?

Demo 演示

( Try it , uncomment the desired testcase first) 尝试一下 ,首先取消注释所需的测试用例)

#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;
}

EDIT 编辑

Extenal operator overloads actually work (thanks to @Turtlefight): Extenal运算符重载实际上有效(感谢@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;
}

This is clearly a bug, in the sense that the standard doesn't give permission for the comparison to not work. 这显然是一个错误,因为标准不允许比较不起作用。

The cause is more interesting. 原因更有趣。 libstdc++'s tuple is EBO'd in such a way that tuple<N> is actually derived from N if N is an empty class. libstdc ++的元组是以如下方式进行EBO:如果N是空类,则tuple<N>实际上是从N派生的。 When you do a < b we need to do a nonmember and a member lookup for operator< . 当你执行a < b我们需要为operator<执行非成员和成员查找。 The nonmember lookup finds tuple 's operator< as expected. 非成员查找按预期查找tupleoperator<

This member lookup, however, finds operator< in two different bases, so by [class.member.lookup]p6-7 this lookup produces an invalid set and is ill-formed on the spot. 但是,这个成员查找在两个不同的基础中找到operator< ,因此通过[class.member.lookup] p6-7,这个查找产生一个无效的集合并且在现场形成错误。

Clang seems to accept this code by suppressing the error in the member lookup, but I'm not currently seeing anything in the standard that permits this behavior. Clang似乎通过抑制成员查找中的错误来接受此代码,但我目前没有在标准中看到允许此行为的任何内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM