I thought MSVC would be C++ standards-conforming because I turned /fpermissive- on, however it seems to behave differently. I have the following handle class:
#include <iostream>
template <typename T, typename void(*deleter)(T)>
struct MyMoveOnlyHandleType
{
void reset() { deleter(handle); }
T handle;
~MyMoveOnlyHandleType() { reset(); }
};
int main() {
{
MyMoveOnlyHandleType<int, [](int arg) {
std::cout << "Hello";
} > my_handle;
}
}
MSVC compiles this, but GCC and Clang don't. I read about that fpermissive- is automatically on, so I thought it would be standards conforming. Anyway, the weird thing is that MSVC only allows it with the /std:c++17 or higher flag. In C++14 it also fails.
Link to Godbolt showing all three.
The errors given:
Clang:
error: expected a qualified name after 'typename'
GCC:
error: expected nested-name-specifier before 'void'
MSVC before c++17:
error C2975: 'deleter': invalid template argument for 'MyMoveOnlyHandleType', expected compile-time constant expression
I thought MSVC would be C++ standards-conforming because I turned /fpermissive- on, however it seems to behave differently. I have the following handle class:
#include <iostream>
template <typename T, typename void(*deleter)(T)>
struct MyMoveOnlyHandleType
{
void reset() { deleter(handle); }
T handle;
~MyMoveOnlyHandleType() { reset(); }
};
int main() {
{
MyMoveOnlyHandleType<int, [](int arg) {
std::cout << "Hello";
} > my_handle;
}
}
MSVC compiles this, but GCC and Clang don't. I read about that fpermissive- is automatically on, so I thought it would be standards conforming. Anyway, the weird thing is that MSVC only allows it with the /std:c++17 or higher flag. In C++14 it also fails.
Link to Godbolt showing all three.
The errors given:
Clang:
error: expected a qualified name after 'typename'
GCC:
error: expected nested-name-specifier before 'void'
MSVC before c++17:
Share Improve this question edited Mar 19 at 23:41 Zebrafish asked Mar 19 at 23:28 ZebrafishZebrafish 15k3 gold badges66 silver badges153 bronze badges 7 | Show 2 more commentserror C2975: 'deleter': invalid template argument for 'MyMoveOnlyHandleType', expected compile-time constant expression
1 Answer
Reset to default 1Short answer: it's Microsoft's Vexing Parse (not to mistaken for Most Vexing parse), as in cl the parser logic isn't consistent, being hand-written.
Long answer:
In cl parser a type with typename
in front of it still can be considred a type-name, even in template parameter list. It's an implementation flaw that surfaces in declaration contexts, so for cl
declaration typename void(*deleter)(T)
in template parameter is a definition of function pointer.
GCC and clang correctly follow MVP - anything that can be considered a declaration, is a declaration. As typename void(*deleter)(T)
is incorrect type parameter declaration, not matching pattern typename id
, it's also incorrect definition of template non-type parameter as typename
isn't followed by a nested type-id dependant on T as prt of definition.
But without typename
you're not in clear yet, code demands c++20 as before that lambdas weren't allowed in stateless context, see new answer to Lambda expressions as class template parameters.
/fpermissive-
is not a validcl
command line argument. It's/permissive-
. However even with that option it still compiles. I'm assuming you know how to trivially fix your code, and are just asking whycl
accepts the broken code as is? – Blindy Commented Mar 20 at 0:03typename
, even with/permissive-
. Shorter example:typename void f(){}
.typename void
is just taken as an elaborate spelling ofvoid
. – Igor Tandetnik Commented Mar 20 at 2:03typename
removed, clang and gcc accept your example at C++20 but not at C++17. I'm guessing rules have changed at some point as to whether a lambda is allowed as a non-type template parameter. – Igor Tandetnik Commented Mar 20 at 2:04