Ответ 1
Я не вижу никакого способа обойти это - определите его один раз и повторно используйте его, когда это возможно.
Такое огромное количество специализаций можно избежать, если квалификаторы верхнего уровня. В этом случае мы могли бы использовать std::remove_cv
или std::remove_reference
, удалив все ортогональные квалификаторы на каждом шаге. К сожалению, это неприменимо для функций, как описано в приведенном вами параграфе: например, cv-qualifier является частью типа функции, а не верхнего уровня. void() const
является принципиально другим типом, чем void()
, и, следовательно, оба они должны соответствовать двум различным частным специализациям.
Вы можете сократить все специализации с помощью макросов:
#define REM_CTOR(...) __VA_ARGS__
#define SPEC(var, cv, ref) \
template <typename R, typename... Args> \
struct strip_function_qualifiers<R(Args... REM_CTOR var) cv ref > \
{using type = R(Args... REM_CTOR var);};
#define REF(var, cv) SPEC(var, cv,) SPEC(var, cv, &) SPEC(var, cv, &&)
#define CV(var) REF(var,) REF(var, const) \
REF(var, volatile) REF(var, const volatile)
template <typename> struct strip_function_qualifiers;
CV(()) CV((,...))
Демо.
Boost.PP также возможен:
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/for_each_product.hpp>
#define REF (&&)(&)()
#define CV (const volatile)(const)(volatile)()
#define VAR (())((,...)) // Had to add a comma here and use rem_ctor below,
// otherwise Clang complains about ambiguous ellipses
#define SPEC(r, product) \
template <typename R, typename... Args> \
struct strip_function_qualifiers<R(Args... BOOST_PP_TUPLE_ENUM( \
BOOST_PP_SEQ_ELEM(0, product))) \
BOOST_PP_SEQ_ELEM(1, product) \
BOOST_PP_SEQ_ELEM(2, product)> \
{using type = R(Args... BOOST_PP_TUPLE_ENUM(BOOST_PP_SEQ_ELEM(0, product)));};
template <typename> struct strip_function_qualifiers;
BOOST_PP_SEQ_FOR_EACH_PRODUCT(SPEC, (VAR)(CV)(REF))
Демо. Оба метода не будут получать больше времени при добавлении новых квалификаторов, таких как transaction_safe
или transaction_safe_noinherit
.
Вот модифицированный SPEC
, который также определяет определенные элементы признаков.
#include <type_traits>
#include <boost/preprocessor/tuple/size.hpp>
// […]
#define SPEC(r, product) \
template <typename R, typename... Args> \
struct strip_function_qualifiers<R(Args... BOOST_PP_TUPLE_ENUM( \
BOOST_PP_SEQ_ELEM(0, product))) \
BOOST_PP_SEQ_ELEM(1, product) \
BOOST_PP_SEQ_ELEM(2, product)> \
{ \
using type = R(Args... BOOST_PP_TUPLE_ENUM(BOOST_PP_SEQ_ELEM(0, product))); \
\
private: \
using cv_type = int BOOST_PP_SEQ_ELEM(1, product); \
using ref_type = int BOOST_PP_SEQ_ELEM(2, product); \
public: \
using is_const = std::is_const<cv_type>; \
using is_volatile = std::is_volatile<cv_type>; \
using is_ref_qualified = std::is_reference<ref_type>; \
using is_lvalue_ref_qualified = std::is_lvalue_reference<ref_type>; \
using is_rvalue_ref_qualified = std::is_rvalue_reference<ref_type>; \
using is_variadic = std::integral_constant<bool, \
!!BOOST_PP_TUPLE_SIZE(BOOST_PP_SEQ_ELEM(0, product))>; \
};