Description
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_assertis 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{});andstatic_assertare 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 commented4.2. no warnings if
static_assertis commented x86 MSVC v19.latest: no warnings
Questions:
- Does the
enum_as_byte<EnumT>in the definition ofbarcount as a user-defined conversion or not? - What compiler (or compiler versions) is/are right from the standard point of view?
- What is/are the paragraph(s) from the standard that specify the correct behavior?
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:)
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 ifUis never used after that. (Godbolt.)Finally, you asked:
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 toenum_as_byte<EnumT>.