2013-04-15 4 views
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"); 
} 

、どこでも、この実装はタイプセーフ(可変長引数のva_argで)通常のCがいない間であると言われています。

なぜですか? タイプセーフであることは何を意味し、C printf va_argに比べてこの実装にどのような利点がありますか?

+3

このバージョンでは、フォーマットフラグについてはまったく気にせず、ストリーム演算子を介して印刷するだけです。 – Xeo

+1

'T'は常に実際に渡されるパラメータの型であるという点で型安全です。標準printfはわかりません。 –

+1

これは、ひどい型セーフな 'printf'実装です。これはフォーマット指定子を無視し、誤って解釈し、一時的な値の移動をサポートしません!要するに、これは型保証された関数ですが、その名前にもかかわらず 'printf'の有効な実装ではありません。型の良い 'printf'は、安全であれば' printf'と同じように動作し、ほとんど安全でない場合には未定義に失敗します。 – Yakk

答えて

4

安全、またはタイプセーフなので、あなたはあなたのプログラムが正しく動作するかどうか、ソースコードを見てから伝えることができることを意味します。

xに明確な値が設定されていると仮定すると、文std::cout << xは常に正しくなります(つまり、初期化されていないとは限りません)。これは、ソースコードを見ることによって保証できるものです。たとえば、次のコードは、またはランタイム入力に応じて、正確でなくてもよい:

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

場合にのみ場合、これが正しいconstrastにより

、Cは、ない安全です第1引数は、正確に1つの「%s」を含む有効な書式文字列です。

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

+0

+1ランタイム入力依存の実行状態を改善するために、そのサンプルをビートするのは難しいです。そしてその上に優れた答え。 – WhozCraig

5

バリデリックテンプレートバージョンに渡すすべての引数について、それらの型はコンパイル時に認識されます。この知識は関数内に保持されます。次に、各オブジェクトは、を過度にオーバーロードしてcoutに渡されます。渡される各タイプごとに、この関数の別個のオーバーロードがあります。つまり、intを渡すと、ostream::operator<<(int)が呼び出されます。ダブルを渡すと、ostream::operator<<(double)が呼び出されます。もう一度、タイプは保存されます。これらの機能のそれぞれは、適切な方法で各タイプを処理するように特化されています。それはタイプセーフです。

しかし、C printfでは、ストーリーが異なります。型は関数内に保持されません。フォーマット文字列の内容(実行時の値かもしれない)に基づいてそれを把握する必要があります。この関数は、正しい型の文字列が引数の型と一致するために渡されたと仮定するだけです。コンパイラはこれを強制しません。

もう1つの種類の安全性もあります。これは引数の数です。 C printf関数に渡す引数が少なすぎると、書式文字列に一致する引数が不足していると、未定義の動作が発生します。バリデーショナルテンプレートで同じことをすると例外が発生しますが、これは望ましくないものの、診断がはるかに簡単な問題です。

関連する問題