Stripping all qualifiers from a function type

孤街醉人 提交于 2019-12-03 10:06:17

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))>; \
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!