c++ - 可变参数模板是潜在的代码膨胀吗?

标签 c++ c++11 variadic-templates

可变参数模板可以将某些类型的函数重写为更简洁、类型安全的版本。这是 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/

相关文章:

c++ - C++ 中 vector 的 vector 的使用 & push_back( )

c++ - 如何将 linux 设备路径与 windows 驱动器名称匹配?

c++ - QCache 和 std::shared_ptr

c++ - 存储 snprintf 参数供以后使用

c++ - 使用模板参数添加/删除数据成员?

c++ - 将二维整数数组(与图像相关)调整为特定大小?

c++ - C++0x 仍然可以使用全局运算符 new 显式分配吗?

c++ - 重复使用 prefix++ 运算符时的未定义行为

c++ - 给定大小 N 和类型 T 生成元组的函数

c++ - gcc 和 clang 的多参数包扩展