Here' a piece of code that is treated differently by clang, gcc and msvc compilers:
#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{});
}
Results (links lead to corresponding Compiler Explorer setups):
<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. if bar<Foo>(Foo{});
is commented: no warnings
<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. if static_assert
is commented:
<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. if bar<Foo>(Foo{});
is commented and static_assert is not:
<source>:25:5: error: static assertion failed due to requirement 'false && ""'
static_assert(false && "");
^ ~~~~~~~~~~~
2.3 if both bar<Foo>(Foo{});
and static_assert
are commented::
no warnings
x86-64 gcc 13.1/13.2: no warnings
<source>: In function 'void bar(enum_as_byte<EnumT>)':
<source>:25:25: error: static assertion failed
25 | static_assert(false && "");
| ~~~~~~^~~~~
4.1. same output if bar<Foo>(Foo{}());
is commented
4.2. no warnings if static_assert
is commented
x86 MSVC v19.latest: no warnings
enum_as_byte<EnumT>
in the definition of bar
count as a user-defined conversion or not?You've got too many things going on in that test case. Make a minimal example!
The static_assert(false)
thing is P2593 (C++23), which some compiler releases already support and some don't. We can ignore that.
Your real question seems to be about the intended behavior of [[deprecated]]
when it's applied to a specific specialization instead of to the primary template. Everyone agrees that the deprecation warning should be given for use of that specific specialization.
GCC seems to give the deprecation warning only for uses of the deprecated specialization that aren't dependent on any template parameter (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>();
Whether A<int>
is "really used" or just used as an alias doesn't matter at all; using U = A<int>
is still warned-about, even if U
is never used after that. (Godbolt.)
Finally, you asked:
Does the
enum_as_byte<EnumT>
in the definition ofbar
count as a user-defined conversion or not?
No, "[user-defined] conversion" is something that happens during evaluation; it's not a syntactic property. Since overload resolution never selects bar(enum_as_byte<EnumT>)
, no call to it ever happens, and so nothing ever gets converted to enum_as_byte<EnumT>
.