Проверка типа пакета параметров с помощью enable_if

Поскольку существует ограничение на допустимые непарные вариативные шаблоны, я пытаюсь написать функцию, берущую произвольное количество удвоений, используя enable_if. В сущности, я хочу сделать что-то вроде:

    template<typename... T,
    typename = typename std::enable_if<std::is_convertible<T, double>::value, T>::type>
    foo(T... t){ /* code here */ }

Я предпочитаю поместить значение enable_if в качестве значения по умолчанию для параметра без имени, поскольку моя функция на самом деле является конструктором и не будет иметь возвращаемого значения. Это будет работать для одного параметра, но так как вариативный шаблон T является пакетом параметров, а приведенный выше код недействителен. Итак, как я могу проверить, что каждый параметр конвертируется в double?

Ответ 1

Тройка bool_pack снова.

template<bool...> struct bool_pack;
template<bool... bs> 
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;

Тогда

template<class R, class... Ts>
using are_all_convertible = all_true<std::is_convertible<Ts, R>::value...>;

и, наконец,

template<typename... T,
typename = typename enable_if<are_all_convertible<double, T...>::value>::type>
foo(T... t){ /* code here */}

Ответ 2

Я думаю, что проще было бы использовать std::initializer_list:

foo(std::initializer_list<double> args)
{
    // Your stuff.
}

вместо вариационного шаблона. Может потребоваться использовать {} вместо/в дополнение к ()

Ответ 3

Вот еще одна () версия (в значительной степени вдохновленная T.C. выше):

#include <type_traits>

template <typename To, typename From, typename... R>
struct are_all_convertible {
    constexpr static bool value = std::is_convertible<From,To>::value &&
                                  are_all_convertible<To,R...>::value;
};

template <typename To, typename From>
struct are_all_convertible<To,From> {
    constexpr static bool value = std::is_convertible<From,To>::value;
};

template<typename... T,
typename = typename std::enable_if<are_all_convertible<double, T...>::value>::type>
foo(T... t){ /* code here */}

Ответ 4

Вы можете использовать сложенное выражение в c ++ 17, чтобы сделать то же самое, что и другие ответы, опубликованные здесь, но без хлопот по созданию шаблонов.

#include <type_traits>

template <typename... T, typename = 
    typename std::enable_if<
        (true && ... && std::is_convertible_v<T, ___YOUR_TYPE___>),
        void
    >::type
>
constexpr auto foo(T...) noexcept {
        // your code 
}

Ответ 5

Вот общий подход – TMP для двоичного сгибания, используя С++ 14. Сначала определим основные операции объединения:

#include <type_traits>

struct and_op
{
    using type = bool;
    using identity = std::true_type;
    template <bool A, bool B> static constexpr bool value = A && B;
};

struct or_op
{
    using type = bool;
    using identity = std::false_type;
    template <bool A, bool B> static constexpr bool value = A || B;
};

Теперь фактический механик fold:

template <typename Op, typename Op::type...>
struct fold;

template <typename Op>
struct fold<Op> : Op::identity {};

template <typename Op, typename Op::type Val>
struct fold<Op, Val>
    : std::integral_constant<typename Op::type
    , Val> {};

template <typename Op, typename Op::type Val, typename Op::type... Tail>
struct fold<Op, Val, Tail...>
    : std::integral_constant<typename Op::type
    , Op::template value<Val, fold<Op, Tail...>::value>> {};

Далее нам нужен способ создания унарных признаков из двоичных признаков путем привязки:

template <template <typename, typename> class BPred, typename T>
struct bind_pred
{
    template <typename U>
    struct pred_1st : std::integral_constant<bool, BPred<T, U>::value> {};
    template <typename U>
    struct pred_2nd : std::integral_constant<bool, BPred<U, T>::value> {};
};

Наконец, вспомогательная оболочка для объединения результата применения унарного предиката:

template <typename Op, template <typename> class UPred, typename ...Args>
struct fold_pred : fold<Op, UPred<Args>::value...> {};

Что это. Теперь давайте работать:

template <typename T>
using maybe_double = bind_pred<std::is_convertible, double>::pred_2nd<T>;

#include <iomanip>
#include <iostream>

int main()
{
    std::cout
        << std::boolalpha
        << fold_pred<and_op, maybe_double, int, float>::value << '\n'
        << fold_pred<and_op, maybe_double, int, float, void>::value << '\n';
}

В С++ 17 (или С++ 1z, скорее) вы можете писать прямые решения с меньшим количеством кода благодаря новым складкам. Например:

template <template <typename> class UPred, typename ...Args>
static constexpr bool pred_all = (UPred<Args>::value && ...);
//                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unary fold

Использование:

static_assert(pred_all<maybe_double, int, float>);