2016-12-28 7 views
1

Допустим, у нас есть шаблон функция обертывания (или функция-члена), как это:Явный шаблон конкретизации для переменного числа шаблонов

template <class... T> void f(T... args) { 
    _f(args...); 
} 
void _f(int a1, ...); // variadic old style function 

Если функция _f (определенная в библиотеке третьей стороны, что мы не можем изменить) принимает несколько комбинаций типов параметров, и было бы желательно ограничить возможные параметры на уровне обертывания.
Если у нас есть сотни таких функций с различными возможными типами параметров, это будет слишком громоздким, чтобы вручную определить перегруженные функции с именованными аргументами. Было бы лучше определить такие функции, используя простой макрос со списками возможных типов. Макросы Boost, которые позволяют итерации списков аргументов для построения именованных аргументов, выглядят слишком тяжелыми.
Есть ли элегантный способ ограничить возможные аргументы на уровне декларации?

+1

Oh ... '_f' - это нестандартная вариационная функция старого стиля. '...' не просто представляет что-то, что вы оставили для краткости, это на самом деле особая особенность C++. Да, тогда мой ответ действительно не сработает, поэтому я удалил его. – hvd

+0

Спасибо в любом случае. Кажется, я не слишком точен. –

+0

@hvd Я получил ваш ответ и немного переработал его, используя черты. Возможно, сейчас это работает. – skypjack

ответ

2

Вы можете использовать признаки для определения обслуживаемых списков параметров в зависимости от их типов.
вытекает минимальный, рабочий пример:

void _f(int, ...) {} 

template<typename...> struct accepts; 
template<> struct accepts<int, double, char> {}; 
template<> struct accepts<int, char, int> {}; 

template <class... T> 
auto f(T... args) -> decltype(accepts<T...>{}, void()) { 
    _f(args...); 
} 

int main() { 
    f(0, 0., 'c'); 
    f(0, 'c', 0); 
    // f(0, 0, 0); 
} 

Последний invokation даст вам компиляцию ошибку времени для <int, int, int> не является допустимой специализация структуры accepts.
Это связано с тем, что основной шаблон не определен, и вы можете управлять принятыми списками, в случае необходимости вводить все больше и больше специализаций.


Здесь немного другое решение на основе std::true_type, std::false_type и static_assert:

#include<type_traits> 

void _f(int, ...) {} 

template<typename...> struct accepts: std::false_type {}; 
template<> struct accepts<int, double, char>: std::true_type {}; 
template<> struct accepts<int, char, int>: std::true_type {}; 

template <class... T> 
void f(T... args) { 
    static_assert(accepts<T...>::value, "!"); 
    _f(args...); 
} 

int main() { 
    f(0, 0., 'c'); 
    f(0, 'c', 0); 
    // f(0, 0, 0); 
} 

Преимуществами являются:

  • более значимое сообщение об ошибке во время компиляции (ну, если вы заменяете "!" чем-то значимым)
  • возможность t о явном виде отключить список параметров, унаследовав от std::false_type и документировать его в то же время в случае необходимости

Недостаток заключается в том, что это немного более многословным.

1

Ну, hvd является спот-на. То, что вы действительно хотите, это SFINAE. Но я уже ушел реализовать его с Boost.PP ооочнем ...

#include <boost/preprocessor/seq/for_each_i.hpp> 
#include <boost/preprocessor/comma_if.hpp> 

#define DECL_PARAM(r, data, i, elem) \ 
    BOOST_PP_COMMA_IF(i) elem _ ## i 

#define FWD_PARAM(r, data, i, elem) \ 
    BOOST_PP_COMMA_IF(i) _ ## i 

#define DEF_FN_WRAPPER(name, delegate, params) \ 
    void name(BOOST_PP_SEQ_FOR_EACH_I(DECL_PARAM, ~, params)) { \ 
     delegate(BOOST_PP_SEQ_FOR_EACH_I(FWD_PARAM, ~, params)); \ 
    } 

Заменяется:

void f(int _0 , double _1 , std::string _2) { _f(_0 , _1 , _2); } 
+0

Спасибо, это, безусловно, возможное решение. –