可变参数模板可以将某些类型的函数重写为更简洁、类型安全的版本。这是 printf
的情况,如 Wikipedia 中给出的示例:
void printf(const char *s)
{
while (*s) {
if (*s == '%' && *(++s) != '%')
throw std::runtime_error("invalid format string: missing arguments");
std::cout << *s++;
}
}
template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%' && *(++s) != '%') {
std::cout << value;
++s;
printf(s, args...); // call even when *s == 0 to detect extra arguments
return;
}
std::cout << *s++;
}
throw std::logic_error("extra arguments provided to printf");
}
但是...据我了解模板,它们意味着每种类型组合的代码重复。因此,上述 printf
的可变版本将被复制多次。这对于大型函数或类来说可能很糟糕。
可变参数模板是否与代码重复的标准模板一样危险? 如果是,继承技巧还有用吗?
最佳答案
简短的回答是:“您只为使用的东西付费”的原则仍然和以前一样适用。
通过比较两个假设实现的生成代码可以看到更长的答案,例如
#include <iostream>
template <typename T>
void func1(T& v) {
v = -10;
}
template <typename T1, typename T2>
void func1(T1& v1, T2& v2) {
func1(v1); func1(v2);
}
// More unused overloads....
template <typename T1, typename T2, typename T3>
void func1(T1& v1, T2& v2, T3& v3) {
func1(v1); func1(v2); func1(v3);
}
int main() {
double d;
int i;
func1(d);
func1(i);
std::cout << "i=" << i << ", d=" << d << std::endl;
func1(d,i);
std::cout << "i=" << i << ", d=" << d << std::endl;
}
使用现代编译器,如果您想完全避免模板,这几乎可以减少您编写的内容。在这个“传统的”C++03 模板化代码中,我的 g++ 版本内联(在编译器中,而不是关键字意义上)全部并且没有明显的暗示初始化是通过模板函数中的引用完成的,多次,在不同的方式。
与等效的可变参数方法相比:
#include <iostream>
#include <functional>
void func1() {
// end recursion
}
template <typename T, typename ...Args>
void func1(T& v, Args&... args) {
v = -10;
func1(args...);
}
int main() {
double d;
int i;
func1(d);
func1(i);
std::cout << "i=" << i << ", d=" << d << std::endl;
func1(d,i);
std::cout << "i=" << i << ", d=" << d << std::endl;
}
这也会生成几乎相同的代码 - 一些标签和错位名称与您预期的不同,但是 g++ -Wall -Wextra -S
生成的 asm 的差异(a 4.7快照)没有显着差异。编译器基本上是动态编写您的程序所需的所有重载,然后像以前一样进行优化。
以下非模板代码也产生几乎相同的输出:
#include <iostream>
#include <functional>
int main() {
double d;
int i;
d= -10; i=-10;
std::cout << "i=" << i << ", d=" << d << std::endl;
d= -10; i=-10;
std::cout << "i=" << i << ", d=" << d << std::endl;
}
再次强调,唯一明显的区别是标签和符号名称。
重点是现代编译器可以“做正确的事”,而不会在模板代码中出现太多麻烦。如果您表达的内容在所有模板机制下都很简单,那么输出也会很简单。如果不是,那么输出会更丰富,但如果您完全避免使用模板,输出也会更丰富。
然而,这变得有趣(在我看来)是这样的:我的所有陈述都符合“使用像样的现代编译器”之类的条件。 如果您正在编写可变参数模板,您几乎可以确定您正在使用的编译器是一个不错的现代编译器。没有任何笨重的老旧编译器支持可变参数模板。
关于c++ - 可变参数模板是潜在的代码膨胀吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8077604/