Variadic模板有很多优点,但有时候应该使用C风格的可变参数函数(使用<cstdarg>
)吗?
答案 0 :(得分:24)
如果您提供带有C ++实现的C API,则模板不是API的选项。 Varargs是。
如果您需要支持不支持C ++ 11或更新标准的编译器,则可变参数模板不可用。 Varargs是。
如果您需要编译防火墙。即你需要从头部隐藏函数的实现,然后variadic模板不是一个选项。 Varargs是。
在内存受限系统(嵌入式)上,模板生成的不同函数可能会引入太多膨胀。也就是说,这样的系统通常也是实时的,在这种情况下,由于分支和堆栈的使用,varargs也可能是不可接受的。
答案 1 :(得分:14)
我想添加到excellent answer of @user2079303
varargs也用于一些元编程(例如用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:当实例化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
现在的问题是我们是否可以用可变参数模板替换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更清楚。
答案 2 :(得分:3)
vararg允许使用__attribute__ format
。 E.g。
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