Лучший способ для ограничения шаблонной функции для белых перечисленных типов, SFINAE? - PullRequest
0 голосов
/ 29 января 2019

Я хочу создать черту / интерфейс, например, is_good, и реализовать мои общие функции, ориентированные на его спецификацию.

Я придумаю следующий код, которому помогают некоторые онлайн-ресурсы, касающиеся SFINAE.

template<class C>
struct is_good: std::false_type
{
};

template<>
struct is_good<A<double, 2>>: std::true_type
{
};

template<>
struct is_good<B<double, 2, 3>>: std::true_type
{
};

template<template<class> class C, class T>
using check_constraints = std::enable_if_t<C<T>::value, bool>;
}

Мои общие функции определены следующим образом:

template<class T, template<class> class C = is_good,
    typename = check_constraints<C, T>>
void compute(const T& t) {}

При использовании

// works
compute(A<double,2>());
compute(B<double,2, 3>());
// should not compile.
compute(B<double,5, 6>());

Однако, это кажется немного громоздким.И я должен добавить

 template<class T, template<class> class C = is_good,
          typename = check_constraints<C, T>>

ко всем моим функциям, которые я планирую обобщить.

Есть ли лучшие способы?

Обновление

Основой этого вопроса является то, что я знаю, что тело моих функций работает с типом A & B & C, как я могу определить свои функции?

Например, на других языках, возможно, вы можете сделать

using T = Union{A, B, C};
void compute(T t){...}
# works with A
compute(A());
# works with B
compute(B());

независимо от того, как выглядит A, B, C.

Ответы [ 3 ]

0 голосов
/ 29 января 2019

Получив свои черты, вы можете просто использовать SFINAE:

template<class T>
std::enable_if_t<is_good<T>>
compute(const T& t) {}

или

template<class T, std::enable_if_t<is_good<T>, int> = 0>
void compute(const T& t) {}
0 голосов
/ 29 января 2019

Однако, это кажется немного громоздким.И я должен добавить

template<class T, template<class> class C = is_good, typename = check_constraints<C, T>>

ко всем моим функциям, которые я планирую обобщить.

Есть ли лучшие способы?

Я считаю макросы в стиле C дистиллированным злом, но для такой проблемы я предполагаю, что макрос для этой сигнатуры шаблона может быть разумным.

В любом случае, я предлагаю немногочастичное улучшение.

С вашим фактическим стилем функции, с выводимым типом (T) перед параметром шаблона-шаблона по умолчанию (C)

template<class T, template<class> class C = is_good,
    typename = check_constraints<C, T>>
void compute(const T& t) {}

, если вы хотите явнопараметр шаблона-шаблона, отличный от значения по умолчанию (is_good), вы должны явно указать как выводимый тип, так и параметр шаблона-шаблона

compute<B<double, 2, 3>, is_good_i_hope>(B<double, 2, 3>);

, потеряв T из аргумента функции.

Но если вы выражаете параметр шаблона-шаблона перед выводимым типом

template <template<typename> class C = is_good, typename T,
          typename = check_constraints<C, T>>
void compute (T const &)
 { } 

вы, когда вы хотите явно указать параметр шаблона-шаблона, отличный от значения по умолчаниюодин, может явным образом только C

compute<is_good_i_hope>(B<double, 2, 3>);

с сохранением вычета T.

0 голосов
/ 29 января 2019

Непонятно в вашем вопросе.Но если is_2d должен присутствовать во всех функциях, почему бы не переместить его в check_constraints?Ссылка: https://gcc.godbolt.org/z/s9Jql-

template<class T>
using check_constraints = std::enable_if_t<is_2d<T>::value, bool>;

template<class T, typename = check_constraints<T>>
void compute(const T& t){
    works(t);
}

Обновление

Если вы просто хотите, это список разрешенных типов, лучше выполнить следующее, так как у вас есть c++ 17.https://gcc.godbolt.org/z/kJN7hu

#include<type_traits>

template<class... Types>
struct TypeContainer{
    template<class T>
    using contains = typename std::disjunction< std::is_same<T,Types>... >::type;
};

using allowed_types = TypeContainer<
    int, 
    float
>;

template<class T, typename = std::enable_if_t<allowed_types::contains<T>::value>>
void compute(const T& t);

void foo() {
    int i;
    compute(i);
    //char c; compute(c);
}
...