c – 可变参数模板是否具有潜在的代码膨胀性?

可变参数模板可以将某些类型的函数重写为更清晰,类型安全的版本.这是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");
}

但是……就我理解模板而言,它们意味着每种类型组合的代码重复.因此上述printfs的可变版本将被复制多次.这对于大型函数或类来说可能很糟糕.

可变参数模板是否与代码复制的标准模板一样危险?
如果是的话,继承技巧仍然有用吗?

解决方法

简短的回答是:“你只支付所用的费用”原则仍然与以前完全一样.

通过比较两个假设实现的生成代码可以看出更长的答案,例如

#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 inlines版本(在编译器中,而不是关键字意义上)整个很多,并且没有明显暗示初始化是通过模板函数中的引用完成的,有几次,以不同的方式完成.

与等效的可变方法相比:

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

这也产生几乎相同的代码 – 一些标签和受损名称与您期望的不同,但是由g -Wall -Wextra -S(4.7快照)生成生成的asm的差异没有显着差异.编译器基本上是编写程序所需的所有重载,然后像以前一样进行优化.

以下非模板代码也产生几乎相同的输出

#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+⬑ * 默认成员函数 原来C++类中,有6个默认成员函数: 构造函数 析构函数 拷贝...
#pragma once // 1. 设计一个不能被拷贝的类/* 解析:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运...
C类型转换 C语言:显式和隐式类型转换 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译...
//异常的概念/*抛出异常后必须要捕获,否则终止程序(到最外层后会交给main管理,main的行为就是终止) try...
#pragma once /*Smart pointer 智能指针;灵巧指针 智能指针三大件//1.RAII//2.像指针一样使用//3.拷贝问...
目录&lt;future&gt;future模板类成员函数:promise类promise的使用例程:packaged_task模板类例程...