GCC无法区分operator ++()和operator ++(int)

时间:2019-01-08 12:07:46

标签: c++ operator-overloading multiple-inheritance ambiguous

template <typename CRTP>
struct Pre {
    CRTP & operator++();
};

template <typename CRTP>
struct Post {
    CRTP operator++(int);
};

struct Derived
    : Pre<Derived>
    , Post<Derived>
{};

int main() {
    Derived d;
    d++;
    ++d;
}

我从GCC收到这些错误:

<source>: In function 'int main()':
<source>:18:10: error: request for member 'operator++' is ambiguous
        d++;
        ^~
<source>:8:14: note: candidates are: CRTP Post<CRTP>::operator++(int) [with CRTP = Derived]
        CRTP operator++(int);
            ^~~~~~~~
<source>:3:16: note:                 CRTP& Pre<CRTP>::operator++() [with CRTP = Derived]
        CRTP & operator++();
                ^~~~~~~~
<source>:19:11: error: request for member 'operator++' is ambiguous
        ++d;
        ^
<source>:8:14: note: candidates are: CRTP Post<CRTP>::operator++(int) [with CRTP = Derived]
        CRTP operator++(int);
            ^~~~~~~~
<source>:3:16: note:                 CRTP& Pre<CRTP>::operator++() [with CRTP = Derived]
        CRTP & operator++();
                ^~~~~~~~

递减前和递减后运算符会导致类似的错误。 Clang没有此类错误。有什么想法可能是错误的,或者如何解决这个问题?

1 个答案:

答案 0 :(得分:63)

必须先进行名称查找。在这种情况下,名称为operator++

  

[basic.lookup] (强调我的意思)

     

1名称查找规则统一适用于所有名称(包括   typedef名称([dcl.typedef]),名称空间名称([basic.namespace]),   和类名([class.name]))在语法允许的地方   在特定规则讨论的上下文中。名称查找助理   使用带有该名称声明([basic.def])的名称。 名称   查找将找到名称的明确声明(请参见   [class.member.lookup])。名称查询可能关联多个   如果发现名称是函数名称,则使用名称进行声明;   声明被声明为一组重载函数   ([超载])。 过载解析([over.match])发生在   名称查找成功。访问规则(子句[class.access])   仅在一次名称查找和函数重载解析时才考虑   (如果适用)已成功。仅在名称查找后起作用   重载解决方案(如果适用)和访问检查已成功   是由名称声明引入的属性,这些属性进一步使用   在表达式处理中(条款[expr])。

并且只有当查询是明确的时,过载解析才会继续进行。在这种情况下,可以在两个不同类的范围内找到该名称,因此甚至在重载解析之前就存在歧义。

  

[class.member.lookup]

     

8如果明确找到了重载函数的名称,   重载分辨率([over.match])也发生在访问之前   控制。通常可以通过以下方式解决歧义:   它的类名。 [示例:

struct A {
  int f();
};

struct B {
  int f();
};

struct C : A, B {
  int f() { return A::f() + B::f(); }
};
     

—结束示例]

该示例几乎总结了[class.member.lookup]前面各段中相当长的查找规则。您的代码中有歧义。海湾合作委员会是正确的举报。


对于解决此问题,评论中的人们已经提出了解决方法的想法。添加一个辅助CRTP类

template <class CRTP>
struct PrePost
    : Pre<CRTP>
    , Post<CRTP>
{
    using Pre<CRTP>::operator++;
    using Post<CRTP>::operator++;
};

struct Derived : PrePost<Derived> {};

现在可以在单个类的范围内找到该名称,并同时命名两个重载。查找成功,并且可能会进行重载解析。