Подтвердить что ты не робот

Удаление всех классификаторов от типа функции

Учитывая возможный тип функции varargs, возможно, cv-qualifier-seq и, возможно, ref-qualifier, можно написать характеристику типа, которая разбивает все квалификаторы без записи 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-seq или ref-qualifier (включая тип, названный typedef-name (7.1.3, 14.1)) только как:

  • тип функции для нестатической функции-члена,
  • тип функции, к которому относится указатель на элемент,
  • тип функции верхнего уровня описания функции typedef или объявления alias,
  • идентификатор типа в аргументе по умолчанию параметра типа (14.1) или
  • идентификатор типа шаблона-аргумента для параметра типа (14.3.1).

[Пример:

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 в функции declarator - это не то же самое, что добавлять cv-квалификацию поверх тип функции. В последнем случае cv-квалификаторы игнорируются. [Примечание: тип функции, имеющий cv-qualifier-seq, не является cv-квалифицированный тип; не существует специальных типов функций cv. - конец примечание] [Пример:

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

- конец примера]

Тип возврата, список параметров-параметров, ref-qualifier и cv-qualifier-seq, но не аргументы по умолчанию (8.3.6) или спецификация исключения (15.4), являются частью тип функции. [Примечание. Типы функций проверяются во время назначения и инициализации указателей на функции, ссылки к функциям и указателям на функции-члены. - конечная нота]

4b9b3361

Ответ 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))>; \
};