I see no way around this - define it once and reuse it whenever possible.
Such a huge amount of specializations is avoidable when the qualifiers are top-level - we could use std::remove_cv
or std::remove_reference
in that case, removing all orthogonal qualifiers in each step. Unfortunately this is not applicable for functions as explained in the paragraph quoted by you: The e.g. cv-qualifier is part of the function type, and not top-level. void() const
is a fundamentally different type than void()
, and thus the both have to be matched by two different partial specializations.
You can shorten all specializations using macros though:
#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((,...))
Demo.
Boost.PP is possible as well:
#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))
Demo. Both methods won't get much longer when adding new qualifiers such as transaction_safe
or transaction_safe_noinherit
.
Here is a modified SPEC
that also defines certain trait members.
#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))>;
};