GCC无法区分运算符++()和运算符++(Int)

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没有这样的错误。您知道可能出了什么问题或如何解决此问题吗?


解决方案

必须先查找名称。在本例中为名称operator++

[basic.lookup](强调我的)

名称查找规则统一应用于所有名称(包括 tyecif-name([dcl.tyecif])、命名空间名称([basic.nampace])、 和类名([class.name])),只要语法允许这样的名称 在由特定规则讨论的上下文中。名称查找助理 使用带有该名称声明([basic.def])的名称。名称 查找应找到名称的明确声明(请参见 [class.ember.lookup])。名称查找可以关联多个 如果发现名称是函数名,则使用该名称进行声明; 这些声明据说形成了一组重载函数 ([over.load])。重载解析([over.Match])发生在 名称查找已成功。访问规则([class.access]条款) 仅考虑一次名称查找和函数重载解析 (如果适用)已成功。仅在名称查找之后,函数 过载解决(如果适用)和访问检查已成功 名称声明引入的属性是否进一步使用 在表达式处理中(子句[EXPR])。

并且只有在查找明确的情况下,才会继续进行重载解析。在这种情况下,名称位于两个不同类的作用域中,因此甚至在重载解析之前就存在歧义。

[class.ember.lookup]

8如果明确找到重载函数的名称, 重载解析([over.Match])也会在访问之前发生 控制室。歧义通常可以通过用来限定名称来解决 它的类名。[示例:

struct A {
  int f();
};

struct B {
  int f();
};

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

-结束示例]

该示例相当程度上总结了[class.ember.lookup]前面段落中相当长的查找规则。您的代码中存在歧义。GCC报道的是对的。


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

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

struct Derived : PrePost<Derived> {};

该名称现在位于单个类的作用域中,并命名两个重载。查找成功,可以继续解决重载问题。

相关文章