最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Why does this compile on MSVC but not GCC or Clang? - Stack Overflow

programmeradmin6浏览0评论

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:

error C2975: 'deleter': invalid template argument for 'MyMoveOnlyHandleType', expected compile-time constant expression

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
  • /fpermissive- is not a valid cl 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 why cl accepts the broken code as is? – Blindy Commented Mar 20 at 0:03
  • @Blindy Yes, why does MSVC compile it even though permissive- is on? I believe it's on by default in newer version, so whether it's added or not shouldn't matter. The point is only after C++17 MSVC accepts it, whereas GCC and Clang reject it. – Zebrafish Commented Mar 20 at 0:24
  • 2 MSVC allows redundant typename, even with /permissive-. Shorter example: typename void f(){}. typename void is just taken as an elaborate spelling of void. – Igor Tandetnik Commented Mar 20 at 2:03
  • With the stray typename 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
  • @IgorTandetnik Do you have a documentation link or a bug link to support your claim? If you have, IMO it's enough to make an answer. – Weijun Zhou Commented Mar 20 at 4:55
 |  Show 2 more comments

1 Answer 1

Reset to default 1

Short 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.

发布评论

评论列表(0)

  1. 暂无评论