Reputation: 168
Please, consider the following code:
template<typename T>
T operator+(const T& lvh, const T& rvh)
{
return lvh;
}
struct enum_container {
enum {
item1 = 1,
item2 = 2,
item3 = item1 + item2
};
};
int main() {
return 0;
}
Compiling with MS VS 2022 gives the following error:
error C2131: expression did not evaluate to a constant
message : failure was caused by call of undefined function or one not declared 'constexpr'
message : see usage of 'operator +'
The problem applies to the third element in enum.
Why cl tries to substitute template for the constant expression?
Compilation with gcc gives no errors.
I can eliminate the error with cl adding type specifier to the enum
, like this:
enum : int {
...
}
But I can`t do this in the project I work with. Both enum and overload declared by 3rd party headers. All I can do is to change includ order. But I'm still wondering why it works that way...
Upvotes: 1
Views: 91
Reputation: 8978
To me it looks like MSVC is correct here.
First, because template argument deduction should happen before overload resolution to form a set of candidate functions:
Before overload resolution begins, the functions selected by name lookup and template argument deduction are combined to form the set of candidate functions
And then, since the non-member candidates (in this case template specialisation of the operator+
overload with the enum_container::<anonymous_enum>
parameters) precede built-in candidates (conversion to the underlying built-in type, for which a user-defined overload would not be possible), the compiler is meant to resolve the overload to the instance of the template with the user-defined enum-type:
- non-member candidates: For the operators where operator overloading permits non-member forms, all declarations found by unqualified name lookup of operator@ in the context of the expression (which may involve ADL), except that member function declarations are ignored and do not prevent the lookup from continuing into the next enclosing scope. If both operands of a binary operator or the only operand of a unary operator has enumeration type, the only functions from the lookup set that become non-member candidates are the ones whose parameter has that enumeration type (or reference to that enumeration type)
- built-in candidates: For
operator,
, theunary operator&
, and theoperator->
, the set of built-in candidates is empty. For other operators built-in candidates are the ones listed in built-in operator pages as long as all operands can be implicitly converted to their parameters. If any built-in candidate has the same parameter list as a non-member candidate that isn't a function template specialization, it is not added to the list of built-in candidates. When the built-in assignment operators are considered, the conversions from their left-hand arguments are restricted: user-defined conversions are not considered.
Thus, the fact that GCC/Clang ignore the existence of the user-defined overload of operator+
under the enum
definition doesn't seem to be compliant with the rules listed above. The same expression inside of a block scope causes exactly same error for all compilers (and I couldn't find any rationale why this might be different for the scope of enum
definition):
struct enum_container {
enum {
item1 = 1,
item2 = 2,
item3 = item1 + item2
};
};
template<typename T>
T operator+(const T& lhs, const T&) {
return lhs;
}
int main() {
// error: call to non-'constexpr' function 'T operator+(const T&, const T&)
constexpr auto enumval = enum_container::item1 + enum_container::item2;
return 0;
}
Upvotes: 1