GCC не может различить operator ++ () и operator ++ (int) - PullRequest
0 голосов
/ 08 января 2019
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 голосов
/ 08 января 2019

Поиск имени должен произойти первым. В данном случае для имени operator++.

[basic.lookup] (выделено мое)

1 Правила поиска имен применяются одинаково ко всем именам (включая typedef-names ([dcl.typedef]), namespace-names ([basic.namespace]), и имена классов ([class.name])) везде, где грамматика допускает такие имена в контексте обсуждается конкретное правило. Название поиска сотрудников использование имени с объявлением ([basic.def]) этого имени. Имя поиск должен найти однозначную декларацию для имени (см. [Class.member.lookup]) . Поиск имени может связывать более одного объявление с именем, если оно находит имя как имя функции; говорят, что объявления формируют набор перегруженных функций ([Over.load]). Разрешение перегрузки ([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]. В вашем коде есть неоднозначность. GCC правильно сообщить об этом.


Что касается обхода этого, люди в комментариях уже представили идеи для обхода проблемы. Добавить вспомогательный класс CRTP

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

struct Derived : PrePost<Derived> {};

Имя теперь находится в области действия одного класса и именует обе перегрузки. Поиск выполнен успешно, и разрешение перегрузки может продолжаться.

...