20

给定一个可能带有cv-qualifier-seq和可能ref-qualifier的 varargs 函数类型,是否可以编写一个类型特征来去除所有限定符而无需编写 4 * 3 * 2 = 24 部分特化?

template<class T>
struct strip_function_qualifiers;

template<class R, class... Args>
struct strip_function_qualifiers<R(Args...)> { using type = R(Args...); };

template<class R, class... Args>
struct strip_function_qualifiers<R(Args..., ...)> { using type = R(Args..., ...); };

template<class R, class... Args>
struct strip_function_qualifiers<R(Args...) const> { using type = R(Args...); };

template<class R, class... Args>
struct strip_function_qualifiers<R(Args..., ...) const > { using type = R(Args..., ...); };

template<class R, class... Args>
struct strip_function_qualifiers<R(Args...) const &> { using type = R(Args...); };

template<class R, class... Args>
struct strip_function_qualifiers<R(Args..., ...) const & > { using type = R(Args..., ...); };

// etc. etc. for each possible combination (24 in total)

随着新的事务性内存 TS的加入transaction_safe,这是否意味着我们需要为此编写 48 个部分特化?


编辑:引用这些奇怪函数类型的描述([dcl.fct]/p6,引用 N4140):

具有cv-qualifier-seqref-qualifier的函数类型 (包括由 typedef-name (7.1.3, 14.1) 命名的类型)应仅显示为:

  • 非静态成员函数的函数类型,
  • 指向成员的指针所指的函数类型,
  • 函数 typedef 声明或alias-declaration的顶级函数类型,
  • 类型参数 (14.1) 的默认参数中的类型ID ,或
  • 类型参数(14.3.1)的模板参数类型 ID

[示例

typedef int FIC(int) const;
FIC f; // ill-formed: does not declare a member function
struct S {
   FIC f; // OK
};
FIC S::*pm = &S::f; // OK

结束示例]

函数声明器中cv-qualifier-seq的效果与在函数类型之上添加 cv-qualification 不同。在后一种情况下,将忽略cv 限定符。[注意:具有cv-qualifier-seq的函数类型不是 cv-qualified 类型;没有 cv 限定的函数类型。—尾注] [示例

 typedef void F();
 struct S {
    const F f; // OK: equivalent to: void f();
 };

结束示例]

返回类型、parameter-type-listref-qualifiercv-qualifier-seq,但不是默认参数 (8.3.6) 或异常规范 (15.4),是函数类型的一部分。[注意:函数类型在函数指针、函数引用和成员函数指针的赋值和初始化过程中被检查。——尾注]

4

1 回答 1

11

我认为没有办法解决这个问题 - 定义一次并尽可能重用它。当限定符是顶级的
时,如此大量的专业化是可以避免的——我们可以使用或者在这种情况下,在每个步骤中删除所有正交限定符。不幸的是,这不适用于您引用的段落中解释的函数: eg cv-qualifier 是函数类型的一部分,而不是顶级。是与 根本不同的类型,因此两者都必须由两个不同的偏特化来匹配。std::remove_cvstd::remove_referencevoid() constvoid()

您可以使用宏缩短所有专业化:

#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添加新的限定符(例如or时,这两种方法都不会变得更长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))>; \
};
于 2015-01-02T15:41:12.023 回答