Поиск имени должен произойти сначала. В этом случае для имени operator++
.
[basic.lookup] (шахта акцента)
1 правила поиска имени применяются однородно ко всем именам (включая имена определения типа ([dcl.typedef]), имена пространства имен ([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 корректен для создания отчетов об этом.
<час>Что касается работы вокруг этого, люди в комментариях уже представили идеи для обходного решения. Добавьте помощника класс
template <class CRTP>
struct PrePost
: Pre<CRTP>
, Post<CRTP>
{
using Pre<CRTP>::operator++;
using Post<CRTP>::operator++;
};
struct Derived : PrePost<Derived> {};
CRTP, которым имя теперь найдено в пределах единого класса и называет обе перегрузки. Поиск успешен, и разрешение перегрузки может продолжиться.