Подтвердить что ты не робот

Есть ли случай, когда функции vararg предпочтительнее, чем вариативные шаблоны?

Шаблоны Variadic имеют много преимуществ, но есть ли случаи, когда вместо этого следует использовать вариативные функции C-стиля (используя <cstdarg>)?

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Я хочу добавить к отличный ответ @user2079303

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

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

Упрощенная современная реализация будет идти так (это не единственный способ: как указано, void_t также может чтобы использовать эту черту, и в ближайшее время (2020?) нам не понадобятся какие-либо из этих трюков как понятия находятся на пути к предложению 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: при создании экземпляра test, если семантика недействительна из-за некоторого зависимое имя, то это не является жесткой ошибкой, вместо этого перегрузка просто игнорируется.

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

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

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

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

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

Теперь возникает вопрос, можем ли мы заменить тест varargs на вариативные шаблоны:

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;
};

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

Is_constructible_broken<X, int, int>::value

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

ошибка: вызов перегруженного 'test(int, int)' неоднозначен

поскольку обе перегрузки жизнеспособны, и оба имеют одинаковый "ранг" при разрешении перегрузки. Первая реализация с 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;
};

Но я думаю, что varargs более ясен в этом случае.

Ответ 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