5

可变参数模板 printf 函数有多种实现。一个是这样的:

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, const T& value, const Args&... args) {
  while (*s) {
    if (*s == '%' && *++s != '%') {
      std::cout << value;
      return printf(++s, args...);
    }
    std::cout << *s++;
  }
  throw std::runtime_error("extra arguments provided to printf");
}

并且到处都说这个实现是类型安全的,而普通的 C(带有可变参数 va_arg)不是。

这是为什么?类型安全是什么意思?与 C printf va_arg 相比,这种实现有什么优势?

4

2 回答 2

5

对于您传递给可变参数模板版本的所有参数,它们的类型在编译时是已知的。该知识保存在函数中。然后将每个对象传递给cout重载的operator<<. 对于传递的每种类型,此函数都有一个单独的重载。换句话说,如果你传递一个int,它就是调用ostream::operator<<(int),如果你传递一个双精度,它就是调用ostream::operator<<(double)。同样,类型被保留。这些函数中的每一个都专门用于以适当的方式处理每种类型。这就是类型安全。

但是,有了 C printf,情况就不同了。该类型不保留在函数内部。它需要根据格式字符串的内容(可能是运行时值)来计算。该函数只需假设传入了正确的格式字符串以匹配参数类型。编译器不强制执行此操作。

还有另一种安全性,那就是论点的数量。如果您向 C 函数传递的参数太少printf,不足以匹配格式字符串,则您的行为未定义。如果你对可变参数模板做同样的事情,你会得到一个异常,虽然不是可取的,但它是一个更容易诊断的问题。

于 2013-04-15T17:02:29.843 回答
4

安全类型安全意味着您可以通过查看源代码来判断您的程序是否正确运行。

该语句std::cout << x总是正确的,假设它x具有明确定义的值(并且不是,例如,未初始化);您可以通过查看源代码来保证这一点。

相比之下,C 是不安全的:例如,以下代码可能正确也可能不正确,具体取决于运行时输入

int main(int argc, char * argv[])
{
    if (argc == 3)
        printf(argv[1], argv[2]);
}

当且仅当第一个参数是一个有效的格式字符串时,这是正确的,它恰好包含一个“ %s”。

换句话说,可以编写一个正确的 C 程序,但不可能仅通过检查代码来推断其正确性。该printf函数就是一个这样的例子。更一般地说,任何接受可变参数的函数很可能是不安全的,任何基于运行时值转换指针的函数也是如此。

于 2013-04-15T17:01:53.373 回答