使用不同编译器的函数模板实例化的不同行为

Different behavior of function template instantiation using different compilers

提问人:Vladislavs Burakovs 提问时间:9/30/2023 更新时间:9/30/2023 访问量:59

问:

描述

这是一段代码,clang、gcc 和 msvc 编译器对其进行了不同的处理:

#include <type_traits>  

template <bool> struct enum_as_byte_check;
template <> struct 
[[deprecated("deprecated warning")]] enum_as_byte_check<true> {};

template <class T>
using is_scoped_enum = std::integral_constant<bool, !std::is_convertible<T,int>{}
                                                  && std::is_enum<T>{}>;
template<class EnumT>
class enum_as_byte
{
    typedef enum_as_byte_check<is_scoped_enum<EnumT>::value> Check;
};

    
template<typename EnumT>
static inline void bar(const EnumT)
{
}

template<typename EnumT>
static inline void bar(enum_as_byte<EnumT>)
{
    static_assert(false && "");
}

enum class Foo : int { };
enum Bar {};
static_assert(is_scoped_enum<Foo>::value);
static_assert(!is_scoped_enum<Bar>::value);

int main() 
{
    bar<Foo>(Foo{});
}

结果(指向相应 Compiler Explorer 设置的链接):

  1. x86-64 叮叮当当 17.0.1

    <source>:13:10: warning: 'enum_as_byte_check<true>' is deprecated: deprecated warning [-Wdeprecated-declarations]
    13 |         typedef enum_as_byte_check<is_scoped_enum<EnumT>::value> Check;
        |                 ^
    <source>:35:14: note: in instantiation of template class 'enum_as_byte<Foo>' requested here
    35 |     bar<Foo>(Foo{});
        |              ^
    <source>:35:5: note: while substituting deduced template arguments into function template 'bar' [with EnumT = Foo]
    35 |     bar<Foo>(Foo{});
        |     ^
    <source>:5:3: note: 'enum_as_byte_check<true>' has been explicitly marked deprecated here
        5 | [[deprecated("deprecated warning")]] enum_as_byte_check<true> {};
        |   ^
    

    1.1. 如果被注释:无警告bar<Foo>(Foo{});

  2. x86-64 叮叮当当 16.0.0

    <source>:25:5: error: static assertion failed due to requirement 'false && ""'
        static_assert(false && "");
        ^             ~~~~~~~~~~~
    <source>:13:10: warning: 'enum_as_byte_check<true>' is deprecated: deprecated warning [-Wdeprecated-declarations]
            typedef enum_as_byte_check<is_scoped_enum<EnumT>::value> Check;
                    ^
    <source>:35:14: note: in instantiation of template class 'enum_as_byte<Foo>' requested here
        bar<Foo>(Foo{});
                ^
    <source>:35:5: note: while substituting deduced template arguments into function template 'bar' [with EnumT = Foo]
        bar<Foo>(Foo{});
        ^
    <source>:5:3: note: 'enum_as_byte_check<true>' has been explicitly marked deprecated here
    [[deprecated("deprecated warning")]] enum_as_byte_check<true> {};
    ^
    

    2.1. 如果static_assert被注释

    <source>:13:10: warning: 'enum_as_byte_check<true>' is deprecated: deprecated warning [-Wdeprecated-declarations]
            typedef enum_as_byte_check<is_scoped_enum<EnumT>::value> Check;
                    ^
    <source>:35:14: note: in instantiation of template class 'enum_as_byte<Foo>' requested here
        bar<Foo>(Foo{});
                ^
    <source>:35:5: note: while substituting deduced template arguments into function template 'bar' [with EnumT = Foo]
        bar<Foo>(Foo{});
        ^
    <source>:5:3: note: 'enum_as_byte_check<true>' has been explicitly marked deprecated here
    [[deprecated("deprecated warning")]] enum_as_byte_check<true> {};
    ^
    

    2.2. 如果被评论而static_assert不是:bar<Foo>(Foo{});

    <source>:25:5: error: static assertion failed due to requirement 'false && ""'
        static_assert(false && "");
        ^             ~~~~~~~~~~~
    

    2.3 如果两者都被注释:: 无警告bar<Foo>(Foo{});static_assert

  3. x86-64 GCC 13.1/13.2:无警告

  4. x86-64 GCC 12.3 版本:

    <source>: In function 'void bar(enum_as_byte<EnumT>)':
    <source>:25:25: error: static assertion failed
    25 |     static_assert(false && "");
        |                   ~~~~~~^~~~~
    

    4.1. 相同的输出 if 被注释bar<Foo>(Foo{}());

    4.2. 如果被评论,则不发出警告static_assert

  5. x86 MSVC v19.latest:无警告

问题:

  • 的定义中是否算作用户定义的转换?enum_as_byte<EnumT>bar
  • 从标准的角度来看,什么编译器(或编译器版本)是正确的?
  • 标准中指定正确行为的段落是什么?
C++ 编译器错误 语言律师 模板实例化

评论


答:

0赞 Quuxplusone 9/30/2023 #1

在那个测试用例中,你有太多的事情要做。举个最小的例子!

问题是P2593(C++23),有些编译器版本已经支持,有些则不支持。我们可以忽略这一点。static_assert(false)

您的真正问题似乎是关于何时将其应用于特定专业化而不是主模板的预期行为。每个人都同意,对于使用该特定专业化,应发出弃用警告。[[deprecated]]

GCC 似乎只对不依赖于任何模板参数 (Godbolt:)的已弃用专用化的使用发出弃用警告

template<class> struct A;
template<> struct [[deprecated]] A<int> {};

template<class T>
void a() { A<T> t; }  // Clang warns (in phase 2), GCC never does

template<class T>
void b() { A<int> t; }  // Both warn (in phase 1)

void c() { A<int> t; }  // Both warn

template void a<int>();
template void b<float>();

是“真正使用”还是仅用作别名根本不重要; 仍然被警告,即使在那之后从未使用过。(神螺栓。A<int>using U = A<int>U)


最后,你问:

的定义中是否算作用户定义的转换?enum_as_byte<EnumT>bar

不,“[用户定义的]转化”是在评估过程中发生的;它不是语法属性。由于重载解析从不选择,因此不会发生对它的调用,因此不会将任何内容转换为 。bar(enum_as_byte<EnumT>)enum_as_byte<EnumT>