5

可変個引数テンプレートの printf 関数にはいくつかの実装があります。1つはこれです:

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)れ、double を渡すと が呼び出されostream::operator<<(double)ます。繰り返しますが、型は保持されます。そして、これらの各関数は、各タイプを適切な方法で処理するように特化されています。それが型安全です。

ただし、 Cprintfの場合は話が異なります。型は関数内で保持されません。フォーマット文字列の内容 (実行時の値である可能性があります) に基づいて、それを把握する必要があります。関数は、引数の型と一致するように正しい書式文字列が渡されたと想定する必要があります。コンパイラはこれを強制しません。

別の種類の安全性もあり、それは引数の数です。C 関数に渡す引数が少なすぎてprintf、フォーマット文字列に一致しない場合、未定義の動作が発生します。可変個引数テンプレートで同じことを行うと、例外が発生します。これは望ましくありませんが、診断がはるかに簡単な問題です。

于 2013-04-15T17:02:29.843 に答える
4

safe 、またはtype-safeであることは、ソース コードを見て、プログラムが正しく動作するかどうかを判断できることを意味します。

適切に定義された値を持っている (そして、初期化されていないなど) とstd::cout << x仮定すると、ステートメントは常に正しいです。xこれは、ソース コードを見れば保証できることです。

対照的に、C は安全ではありません。たとえば、次のコードは実行時の入力に応じて正しい場合と正しくない場合があります。

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

これは、最初の引数が " " を正確に 1 つ含む有効な書式文字列である場合にのみ正しいです%s

つまり、正しい C プログラムを作成することはできますが、コードを検査するだけでは正しいかどうかを判断することはできません。関数はそのprintf一例です。より一般的には、可変引数を受け入れる関数は、実行時の値に基づいてポインターをキャストする関数と同様に、安全ではない可能性が最も高くなります。

于 2013-04-15T17:01:53.373 に答える