Как я могу избежать записи `:: value` и`:: type` при использовании `std:: enable_if`? [Cppx]
Примечание. Это вопрос с ответом, чтобы документировать технику, которую другие могут найти полезной, и для того чтобы быть осведомленным о других & rsquo; даже лучшие решения. Не стесняйтесь добавлять критику или вопросы в качестве комментариев. Также не стесняйтесь добавлять дополнительные ответы.:)Суб > В некоторых моих кодах, а именно в заголовке rfc/cppx/text/String.h
, я нашел следующий таинственный фрагмент:
template< class S, class enable = CPPX_IF_( Is_a_< String, S > ) >
void operator! ( S const& )
{ string_detail::Meaningless::operation(); }
operator!
поддерживает класс String
, который имеет неявное преобразование в исходный указатель. Поэтому я перегружаю (среди прочих) operator!
для этого класса и производных классов, так что непреднамеренное использование не поддерживаемого оператора даст подходящую ошибку компиляции, отметив, что это бессмысленно и недоступно. Я думаю, что гораздо предпочтительнее такого использования, которое тихо принимается с неожиданным, неправильным результатом.
Макрос CPPX_IF_
поддерживает Visual С++ 12.0 (2013) и более ранние версии, который обнаруживает, что С++ 11 using
находится в основном за его пределами. Для более стандартного компилятора я бы написал just & hellip;
template< class S, class enable = If_< Is_a_< String, S > > >
void operator! ( S const& )
{ string_detail::Meaningless::operation(); }
Это выглядит как std::enable_if
,
template< class S, class enabled = typename std::enable_if< Is_a_< String, S >::value, void >::type >
void operator! ( S const& )
{ string_detail::Meaningless::operation(); }
за исключением того, что If_
или CPPX_IF_
и его выражение гораздо более кратки и понятны.
Как я это сделал на Земле?
Ответы
Ответ 1
В С++ 14 переменные шаблоны делают свойства типов более удобными для просмотра. Объедините это с псевдонимами шаблонов С++ 11, и все крутилы исчезают:
template <typename A, typename B>
bool is_base_of_v = std::is_base_of<A, B>::value;
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
Использование:
template <typename B, typename D>
enable_if_t<is_base_of_v<B, D>, Foo> some_function(B & b, D & d) { /* ... */ }
Алиасы типа "тип" формы _t
на самом деле планируются как часть стандартной библиотеки для С++ 14, см. [meta.type.synop].
Ответ 2
Средство для компилятора удобного с С++ 11- using
- это просто & hellip;
namespace cppx {
using std::enable_if;
template< class Condition_type, class Result_type = void >
using If_ = typename enable_if<Condition_type::value, Result_type>::type;
} // namespace cppx
Поддержка большего using
-захваченного компилятора, такого как Visual С++ 12.0 и более ранних версий (он понимает базовое использование using
, но становится все более ненадежным, тем более контекст использования имеет такие вещи, как enable_if
), немного более активное участие, основанный на С++ 03-стилевом решении, таком как & hellip;
namespace cppx {
using std::enable_if;
template<
class Condition_type,
class Result_type = void,
class enabled = typename enable_if<Condition_type::value, void>::type
>
struct If_T_
{
typedef Result_type T;
typedef Result_type type;
};
} // namespace cppx
В основном это обеспечивает только более читаемое имя и исключает ::value
в состоянии. Чтобы обойтись без typename
и ::type
, я использую макрос. Но поскольку выражение, как правило, является выражением шаблона, может быть запятой, что препроцессор будет интерпретироваться как разделители аргументов, так что препроцессор может видеть несколько аргументов.
Решение, которое я использую для этого (время С++ 03 для меня закончено), заключается в использовании C99/С++ 11 вариационного макроса, & hellip;
#define CPPX_IF_( ... ) \
typename cppx::If_T_< __VA_ARGS__ >::T
Соответствующий макрос может быть определен для использования этой функции без typename
.
Полный список, файл rfc/cppx/utility/If_.h
:
#pragma once
// Copyright (c) 2013 Alf P. Steinbach
#include <type_traits> // std::enable_if
#define CPPX_IF_( ... ) \
typename cppx::If_T_< __VA_ARGS__ >::T
namespace cppx {
using std::enable_if;
template< class Condition_type, class Result_type = void >
using If_ = typename enable_if<Condition_type::value, Result_type>::type;
template<
class Condition_type,
class Result_type = void,
class enabled = typename enable_if<Condition_type::value, void>::type
>
struct If_T_
{
typedef Result_type T;
typedef Result_type type;
};
} // namespace cppx
Кроме того, для полноты, Is_a_
определяется просто как & hellip;
template< class Base, class Derived_or_eq >
using Is_a_ = std::is_base_of<Base, Derived_or_eq>;
который использует using
, что понимает Visual С++ 12.0.
Чтобы использовать compund conditions без записи ::value
везде, пригодится следующее определение. По сути, это логические операторы, которые работают с типами. Возможно, стоит особо отметить общий эксклюзивный оператор OR, который не реализован в терминах двоичного XOR (например, !=
): это дало бы оператор, который проверил бы нечетное число значений true
, что мало практично кроме специального случая, состоящего из двух аргументов.
namespace cppx {
using std::integral_constant;
template< bool c >
using Bool_ = integral_constant<bool, c>;
using False = Bool_<false>; // std::false_type;
using True = Bool_<true>; // std::true_type;
template< bool v, class First, class... Rest >
struct Count_
{
enum{ value = Count_<v, First>::value + Count_<v, Rest...>::value };
};
template< bool v, class X >
struct Count_<v, X>
{
enum{ value = int(!!X::value == v) };
};
template< class X >
using Not_ = Bool_<Count_<true, X>::value == 0>; // NOT
template< class... Args >
using All_ = Bool_<Count_<false, Args...>::value == 0>; // AND
template< class... Args >
using Some_ = Bool_<Count_<true, Args...>::value != 0>; // General inclusive OR.
template< class... Args >
using Either_ = Bool_<Count_<true, Args...>::value == 1>; // General exclusive OR.
} // namespace cppx
Отказ от ответственности: ни один из кода не был широко протестирован, а приемы компилятора С++ в области метапрограммирования шаблонов являются общими.
Ответ 3
У нас есть решения С++ 03 и С++ 11 и С++ 14, но Concepts Lite отсутствует:
template <typename Derived, typename Base>
constexpr bool Is_a_() {
return std::is_base_of<Base, Derived>::value;
}
template<Is_a_<String> S>
void operator! ( S const& )
{ string_detail::Meaningless::operation(); }
или еще более кратким:
template <typename Derived, typename Base>
concept bool Is_a_() {
return std::is_base_of<Base, Derived>::value;
}
void operator! ( Is_a_<String> const& )
{ string_detail::Meaningless::operation(); }
Я настоятельно рекомендую ознакомиться с учебником (раздел 2) документа Concepts Lite, чтобы понять, насколько лучше мир будет после того, как мы освободимся от наших подчиненных enable_if
.