(Hi but first I am really sorry about the previous messages, I can't undo the
mail sending. I am new to GCC and I was just hot-headed after finding ways
different from current implementation and didn't organize my mind well.)
First about correctness,
"libstdc++-v3/testsuite/20_util/is_scoped_enum/value.cc" gives excellent
potential counterexamples.
Then it's the time cost of compilation, I use my own bench for 2 hours to find
that `requires(remove_cv_t<_Tp> __t) { __t = __t; }` is surprisingly faster
than `requires { sizeof(_Tp); }` ! And what's more, `requires(_Tp __t,
void(*__f)(int)) { __f(__t); }` is also faster than `requires(int __i) { __i =
_Tp{}; }`.
But! I find that `requires(void(*__f)(int)) { __f(_Tp{}); }` is faster than
`requires(_Tp __t, void(*__f)(int)) { __f(__t); }` !
Another surprise is that, `requires(remove_const_t<_Tp> __t) { __t = __t; }` is
slower than `requires(remove_cv_t<_Tp> __t) { __t = __t; }`, though both pass
the correctness test.
I am not sure if it's necessary to change `is_scoped_enum_v` as well, since
with modules I can't detect the speed differences, maybe my tools are too naive
and can't handle micro glitches.
So here's the proposed modification, but you may as well reject this, because
it hardly makes any key difference in compilation speed:
Old code:
# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_scoped_enum)
template<typename _Tp>
struct is_scoped_enum
: bool_constant<__is_scoped_enum(_Tp)>
{ };
# else
template<typename _Tp>
struct is_scoped_enum
: false_type
{ };
template<typename _Tp>
requires __is_enum(_Tp)
&& requires(remove_cv_t<_Tp> __t) { __t = __t; } // fails if incomplete
struct is_scoped_enum<_Tp>
: bool_constant<!requires(_Tp __t, void(*__f)(int)) { __f(__t); }>
{ };
# endif
New code:
# if _GLIBCXX_USE_BUILTIN_TRAIT(__is_scoped_enum)
template<typename _Tp>
struct is_scoped_enum
: bool_constant<__is_scoped_enum(_Tp)>
{ };
# else
template<typename _Tp>
struct is_scoped_enum
: false_type
{ };
template<typename _Tp>
requires __is_enum(_Tp)
&& requires(remove_cv_t<_Tp> __t) { __t = __t; } // fails if incomplete.
struct is_scoped_enum<_Tp>
: bool_constant<!requires(void(*__f)(int)) { __f(_Tp{}); }>
{ };
# endif
// Copyright (C) 2021-2025 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-do compile { target c++23 } }
// { dg-add-options no_pch }
#include <type_traits>
#ifndef __cpp_lib_is_scoped_enum
# error "Feature test macro for is_scoped_enum is missing in <type_traits>"
#elif __cpp_lib_is_scoped_enum < 202011L
# error "Feature test macro for is_scoped_enum has wrong value in <type_traits>"
#endif
#include "testsuite_tr1.h"
using std::false_type, std::remove_cv_t, std::remove_const_t, std::bool_constant;
template<typename _Tp>
struct is_scoped_enum
: false_type
{ };
template<typename _Tp>
requires __is_enum(_Tp)
&& requires(remove_cv_t<_Tp> __t) { __t = __t; } // fails if incomplete; or `remove_const_t' ?
struct is_scoped_enum<_Tp>
: bool_constant<!requires(void(*__f)(int)) { __f(_Tp{}); }>
{ };
template <typename _Tp>
inline constexpr bool is_scoped_enum_v = is_scoped_enum<_Tp>::value;
template<typename T>
concept Is_scoped_enum
= __gnu_test::test_category<::is_scoped_enum, T>(true);
struct Incomplete_struct;
void
test01()
{
enum class E { e1, e2 };
static_assert( Is_scoped_enum<E> );
enum class Ec : char { e1, e2 };
static_assert( Is_scoped_enum<Ec> );
// negative tests
enum U { u1, u2 };
static_assert( ! Is_scoped_enum<U> );
enum F : int { f1, f2 };
static_assert( ! Is_scoped_enum<F> );
static_assert( ! Is_scoped_enum<Incomplete_struct> );
struct S;
static_assert( ! Is_scoped_enum<S> );
struct S { };
static_assert( ! Is_scoped_enum<S> );
static_assert( ! Is_scoped_enum<int> );
static_assert( ! Is_scoped_enum<int[]> );
static_assert( ! Is_scoped_enum<int[2]> );
static_assert( ! Is_scoped_enum<int[][2]> );
static_assert( ! Is_scoped_enum<int[2][3]> );
static_assert( ! Is_scoped_enum<int*> );
static_assert( ! Is_scoped_enum<int&> );
static_assert( ! Is_scoped_enum<int*&> );
static_assert( ! Is_scoped_enum<int()> );
static_assert( ! Is_scoped_enum<int(*)()> );
static_assert( ! Is_scoped_enum<int(&)()> );
}
enum opaque_unscoped : short;
enum class opaque_scoped;
enum class opaque_scoped_with_base : long;
static_assert( ! Is_scoped_enum<opaque_unscoped> );
static_assert( Is_scoped_enum<opaque_scoped> );
static_assert( Is_scoped_enum<opaque_scoped_with_base> );
void
test02()
{
enum unscoped {
u_is_enum = std::is_enum_v<unscoped>,
u_is_scoped = ::is_scoped_enum_v<unscoped>,
};
static_assert( unscoped::u_is_enum );
static_assert( ! unscoped::u_is_scoped );
enum unscoped_fixed : char {
uf_is_enum = std::is_enum_v<unscoped_fixed>,
uf_is_scoped = ::is_scoped_enum_v<unscoped_fixed>,
};
static_assert( unscoped_fixed::uf_is_enum);
static_assert( ! unscoped_fixed::uf_is_scoped );
enum class scoped {
is_enum = std::is_enum_v<scoped>,
is_scoped = ::is_scoped_enum_v<scoped>,
};
static_assert( (bool) scoped::is_enum );
static_assert( (bool) scoped::is_scoped );
}