2016-10-31 3 views

ответ

24
  1. Если вы предоставляете C API с реализацией C++, тогда шаблоны не являются опцией для API. Варгары есть.

  2. Если вам необходимо поддерживать компилятор, который не поддерживает стандарт C++ 11 или более новый, то вариативные шаблоны недоступны. Варгары есть.

  3. Если вам нужен компиляторный брандмауэр. То есть вам нужно скрыть реализацию функции из заголовка, тогда вариационный шаблон не является вариантом. Варгары есть.

  4. В системах с ограниченной памятью (вложенных) различные функции, генерируемые шаблоном, могут вводить слишком много раздувания. Тем не менее, такие системы обычно также являются реальным временем, и в этом случае varargs также может быть неприемлемым из-за разветвления и использования стека.

+10

varargs также используются в некоторых метапрограммировании (черты, реализованные с помощью SFINAE, например) из-за их свойства считаться последним при разрешении перегрузки. – bolov

+0

@bolov круто! Я никогда не сталкивался с этим прецедентом. Звучит немного * слишком * умный для моего хорошего, хотя :) – user2079303

+0

@bolov не могли бы вы дать более подробную информацию, даже пример кода или ссылку –

14

Я хочу добавить к excellent answer of @user2079303

также спискам параметров используется в некотором метапрограммировании (черт реализованное с SFINAE, например) из-за их имущество рассматривается последними на перегруженном.

Допустим, мы хотим реализовать черту, чтобы обнаружить, если класс конструктивна от некоторых типов (что-то вроде std::is_constructible:

Упрощенная современная реализация будет идти, как это (это не единственный способ: как было указано, void_t может также be used to implement the trait и в ближайшее время (? 2020) мы не будем нуждаться в какой-либо из этих уловок, как concepts находятся на пути с пунктом require в качестве первого класса гражданина):

template <class T, class... Args> struct Is_constructible { 

    template <class... Params> 
    static auto test(Params... params) -> decltype(T{params...}, std::true_type{}); 

    static auto test(...) -> std::false_type; 

    static constexpr bool value = decltype(test(std::declval<Args>()...))::value; 
}; 

Это работает из-за SFINAE: когда instantiati ng test, если семантика недействительна из-за некоторого dependent name, тогда это не является жесткой ошибкой, вместо этого перегрузка просто игнорируется.

Если вы хотите узнать больше о трюках-чертах и ​​о том, как они реализованы и как они работают, вы можете прочитать далее: sfinae idiom, member detector idiom, enable_if idiom.

Итак, с типом X, который может быть построен только из 2 Интс:

struct X { X(int, int) {}; }; 

мы получаем следующие результаты:

Is_constructible<X, int, int>::value // true 
Is_constructible<X, int>::value;  // false 

Вопрос теперь заключается в том, можно ли заменить тест списков параметров с вариационные шаблоны:

template <class T, class... Args> struct Is_constructible_broken { 

    template <class... Params> 
    static auto test(Params... params) -> decltype(T{params...}, std::true_type{}); 

    template <class... Params> 
    static auto test(Params...) -> std::false_type; 

    static constexpr bool value = decltype(test(std::declval<Args>()...))::value; 
}; 

И ответ нет (по крайней мере, нет t - прямая замена).Когда мы создаем

Is_constructible_broken<X, int, int>::value 

мы получаем ошибку:

error: call of overloaded ' test(int, int) ' is ambiguous

, потому что оба перегруженных являются жизнеспособными, и оба имеют один и тот же «ранга» в разрешении перегрузки. Первая реализация с varargs работает, потому что, даже если обе перегрузки жизнеспособны, вариационный шаблон является предпочтительным по сравнению с vararg.

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

struct overload_low_priority{}; 
struct overload_high_priority : overload_low_priority {}; 

template <class T, class... Args> struct Is_constructible2 { 

    template <class... Params> 
    static auto test(overload_high_priority, Params... params) 
     -> decltype(T{params...}, std::true_type{}); 

    template <class... Params> 
    static auto test(overload_low_priority, Params...) -> std::false_type; 

    static constexpr bool value 
     = decltype(test(overload_high_priority{}, std::declval<Args>()...))::value; 
}; 

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

+0

Обратите внимание, что 'void_t' может использоваться для создания признаков вместо использования многоточия. – Jarod42

+0

@ Jarod42 да, и скоро это тоже будет устаревшим с помощью [require clause] (http://en.cppreference.com/w/cpp/language/constraints) – bolov

+0

Я предпочитаю 'struct high_overload_priority: low_overload_priority {};' использование над 'int' /' long', чтобы заказать перегрузку. – Jarod42

3

vararg позволяет использовать __attribute__ format. Например.

void debug(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 

void f(float value) 
{ 
    debug("value = %d\n", value); // <- will show warning. 
} 

К сожалению, этого не может быть достигнуто с использованием вариативных шаблонов.

Отредактировано:Как отметил Владимир, я забыл упомянуть, что __attribute__ format не является частью стандарта, однако он поддерживается как GCC, так и Clang (но не Visual Studio). Для получения дополнительной информации см.: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes

+0

Я бы посоветовал вам поставить ссылку на использование формата __attribute__, поскольку он не является стандартным. Я также думаю, что хорошо показать некоторые из компиляторов, которые он использует. Я знаю только GCC. –

+0

https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html –

Смежные вопросы