Законно ли использовать оператор в выражении сгиба, которое было введено объявлением использования?

Я экспериментировал с использованием произвольных функций в выражениях свертки, когда нашел следующий код, который компилируется с помощью gcc но не компилируется с помощью clang.

enum Enum {
    A = 3,
    B = 8,
    C = 5
};

namespace EnumMax {
    constexpr Enum operator>>=(const Enum left, const Enum right) {
        return left < right ? right : left;
    }
}

template<Enum ... enums>
constexpr Enum max() {
    using EnumMax::operator>>=;
    return (enums >>= ...);
}

constexpr Enum max_v = max<A, B, C>();

https://godbolt.org/z / -LOudM

Кажется, что clang не учитывает перегруженный оператор, но пытается использовать регулярный оператор >>= в выражении сгиба.

Однако, если вместо этого указано выражение сгиба, clang учитывает перегруженный оператор и прекрасно скомпилирует:

constexpr Enum maxExplicit() {
    using EnumMax::operator>>=;
    return (A >>= (B >>= C));
}

Это clang? Или прописанный эквивалент выражения сгиба не совсем эквивалентен?

Ответы

Ответ 1

За [expr.prim.fold]/складной оператор:

фолд-оператор: один из

+   -   *   /   %   ^   &   |   <<   >> 
+=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=  =
==  !=  <   >   <=  >=  &&  ||  ,    .*   ->*

Так что >>= - это фолд-оператор.

За [expr.prim.fold]/2:

Выражение вида (... op e) где op - оператор сгиба, называется унарной левой складкой. Выражение вида (e op...) где op - оператор сгиба, называется унарным правым сгибом. Одинарные левые складки и одинарные правые складки в совокупности называются одинарными складками. В одинарной складке приведенное выражение должно содержать нерасширенную упаковку ([temp.variadic]).

Итак, (enums >>=...) - одинарное правильное сгибание.

Per [temp.variadic]/10:

Реализация выражения сгиба производит:

  • [...]

  • E 1 op (⋯ op (E N−1 op E N)) для одинарного правого сгиба,

  • [...]

В каждом случае op - это оператор сгиба, N - количество элементов в параметрах расширения пакета, и каждый E i генерируется путем создания экземпляра шаблона и замены каждого параметра расширения пакета его i м элементом. [...]

Следовательно, (enums >>=...) семантически эквивалентны (A >>= (B >>= C)) при создании его экземпляра. Так что это ошибка в Clang.