变异模板和类型安全

时间:2013-04-15 16:40:12

标签: c++ c++11 variadic-templates type-safety

variadic模板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有什么优势?

2 个答案:

答案 0 :(得分:5)

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

虽然C printf但故事却不同。该类型不会保留在函数内部。它需要根据格式字符串的内容(可能是运行时值)来计算出来。该函数必须假定传入了正确的格式字符串以匹配参数类型。编译器不强制执行此操作。

还有另一种安全性,那就是参数的数量。如果向C printf函数传递的参数太少,不足以匹配格式字符串,则表明存在未定义的行为。如果对可变参数模板执行相同的操作,则会出现异常,虽然不可取,但却是一个更容易诊断的问题。

答案 1 :(得分:4)

安全类型安全,意味着您可以从查看源代码判断您的程序是否正常运行。< / p>

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

通过约束,C 安全:例如,以下代码可能正确也可能不正确,取决于运行时输入

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

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

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