在 using 声明引入的折叠表达式中使用运算符是否合法?

Is it legal to use an operator in a fold expression that was introduced by a using declaration?

我正在尝试在折叠表达式中使用任意函数,当我发现以下代码可以用 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没有考虑重载运算符,而是尝试在折叠表达式中使用常规>>=运算符。

然而,如果 fold 表达式被拼写出来,clang 会考虑重载运算符并且编译得很好:

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

这是 clang 错误吗?或者折叠表达式的拼写等价物不完全等价?

根据 [expr.prim.fold]/fold-operator:

fold-operator: one of

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

所以>>=是一个fold-operator.

根据 [expr.prim.fold]/2:

An expression of the form (... <em>op</em> e) where op is a fold-operator is called a unary left fold. An expression of the form (e <em>op</em> ...) where op is a fold-operator is called a unary right fold. Unary left folds and unary right folds are collectively called unary folds. In a unary fold, the cast-expression shall contain an unexpanded pack ([temp.variadic]).

所以(enums >>= ...)是一元右折。

根据 [temp.variadic]/10:

The instantiation of a fold-expression produces:

  • [...]

  • E<sub>1</sub> <em>op</em> (⋯ <em>op</em> (E<sub><em>N</em>−1</sub> <em>op</em> E<sub><em>N</em></sub>)) for a unary right fold,

  • [...]

In each case, op is the fold-operator, N is the number of elements in the pack expansion parameters, and each E<sub><em>i</em></sub> is generated by instantiating the pattern and replacing each pack expansion parameter with its ith element. [...]

因此,(enums >>= ...) 在实例化时在语义上等同于 (A >>= (B >>= C))。所以这是 Clang 中的一个错误。