I currently have this block of code which does not throw a compiler error on GCC, but throws this compiler error on clang:
error: nested name specifier 'MyEnum::' for declaration does not refer into a class, class template or class template partial specialization
I'm not actually too sure why this is the case. In the code example below, I have offered some potential solutions to not have the error being thrown, and my particular use case. Here's the godolt link for a compiled example: https://godbolt.org/z/57aMreM3K
#include <stdexcept>
enum class MyEnum : int {
MIN = 0,
MAX = 1
};
class TestClass {
public:
explicit TestClass(const MyEnum type) {
throw std::runtime_error("Runtime Error");
}
};
int main() {
try {
TestClass(MyEnum::MIN);
} catch(...) { }
// My usecase was to use google test to make sure there's no exception on construction
// EXCEPT_NO_THROW(TestClass(MyEnum::MIN));
// Potential solutions that compile for both GCC/Clang:
// EXCEPT_NO_THROW(auto variable = TestClass(MyEnum::MIN));
// EXCEPT_NO_THROW(TestClass((MyEnum)MyEnum::MIN));
}
Clang is correct here.
There is an ambiguity in the base C++ grammar for your statement
TestClass(MyEnum::MIN);
Knowing only that TestClass
is a valid type-name, but not performing any lookup aside from that, this could be either:
An expression statement which consists of a functional style explicit cast expression that casts the result of the id-expression MyEnum::MIN
to TestClass
.
A declaration with type-specifier TestClass
, without any initializer and with parenthesized qualified declarator-id MyEnum::MIN
. Note that in C and C++ declarators can always be wrapped in parentheses without change in meaning, e.g. int (x) = 5;
is a valid declaration of a variable x
of type int
initialized to 5
.
You want the first meaning. However, the ambiguity is always resolved in favour of a declaration based purely on the syntactic form of the statement and whether or not constituents are valid type-name. See [stm.ambig] in the standard (draft) for the exact rules.
In particular, the declaration interpretation is syntactically possible, but semantically ill-formed, because you can't (re-)declare the enumerator MyEnum::MIN
.
But since the semantics are not considered in the disambiguation, the interpretation as declaration must be chosen and the program is ill-formed as a consequence of the declaration being ill-formed.
That GCC doesn't diagnose the issue is an open bug, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62116.
An easy way of preventing ambiguities of this kind from arising is to use curly braces instead of parentheses for functional-style explicit casts:
TestClass{MyEnum::MIN};
Depending on what you want to test this might not be a solution, because it can change the meaning of the expression.
Some other possibilities to disambiguate as an expression statement which do not affect the meaning of your expression are:
(void)TestClass(MyEnum::MIN);
static_cast<void>(TestClass(MyEnum::MIN));
(TestClass(MyEnum::MIN));
void(), TestClass(MyEnum::MIN);
TestClass(MyEnum::MIN), void();