2011-12-20 7 views
1

最近、私は1つの引数を受け付けるテンプレートコンストラクタを持つクラスを持つときに奇妙な振る舞いに遭遇しました。特定の状況下では、このクラスは初期化に失敗します。非常に奇妙な振る舞い:1つの引数のみを持つテンプレートコンストラクタを持つクラス

#include <iostream> 

struct Foo 
{ 
    template<typename T> 
    Foo(const T& x) 
    { 
     std::cout << "--" << x << std::endl; 
    } 
}; 

int main(int, char**) 
{ 
    Foo f(std::string()); 

    std::cout << "++" << f << std::endl; 
    return 0; 
} 

あなたが見るコンストラクタを持つFoo構造です。 mainでは、空のstd :: stringを持つ新しいFooインスタンスを作成します。この例はコンパイルされ、出力は次のようになります。

++1 

WTF?あなたが気づいたかもしれないので、これにはという3つの問題があります。最初にコンストラクタは呼び出されませんでした.2番目にfは "1"として出力されました.3番目に、オーバーロードされたオペレータ< <は、コンパイラが一度も不平を言わずにcoutを使用してFooを出力します。つまり、fはFooの型ではありません。

私は少し例を変更します。

int main(int, char**) 
{ 
    Foo f(std::string("")); 

    std::cout << "++" << f << std::endl; 
    return 0; 
} 

...と、コンパイラはそこには、オーバーロードオペレータ< <はありません、それが正常であることを示すエラーがスローされます。

--Hello 

、それが再び普通です:

int main(int, char**) 
{ 
    Foo f(std::string("Hello")); 
    return 0; 
} 

今出力されます。

問題は、std :: string()またはfloat()またはany_type()であるかどうかにかかわらず、Foo :: Fooに空のオブジェクトを渡すときです。私はGCC 4.4と4.6でその例をテストしましたが、私はこの動作が非常に予期しないものであることを発見しました。

あなたの意見は?これはGCCのバグですか、私はここに何か不足していますか?

+0

私はそれが関数ポインタとして 'f'を解釈していると思いますが、なぜ私は明確ではありません。たとえば、これも 'Foo f = Foo(std :: string);' –

+0

の例をコピーして貼り付けます.Video Studio 2010ではリンクされません。 –

答えて

5
Foo f(std::string()); 

あなたが考えているようだとして、それは、タイプFooのインスタンスを作成しません。

代わりに、これは、その戻り型Fooである名前fの機能を宣言し、パラメータタイプは(引数を取らず、std::string返す)関数型です。

それは同じである。この唯一の違い

Foo f(std::string (*)()); //same as yours 

とあなたの関数の宣言は、あなたのケースでパラメータの型は、最終的にとにかく機能ポインタタイプに崩壊関数型(であるということです)、私のケースでは、パラメータタイプは関数ポインタタイプです(ポインタタイプに崩壊する必要はありません)。

++1を印刷するのは、void*を引数とするoperator<<を呼び出して、この関数のアドレスを出力するからです。あなたがオブジェクトを宣言する場合

今、あなたのように余分な括弧を使用する必要があります。

Foo f((std::string())); //notice the extra parens! 

これは、オブジェクトを宣言し、コンストラクタを呼び出します。http://ideone.com/Px815


もこれを参照してください。トピック:

+0

ideoneへのリンクが壊れているようです。私だけ? – TobiMcNamobi

2

あなたは悪名高いC++ Most vexing parseを経験しています。あなたの最初の例では

Foo f(std::string()); 

は、コンストラクタが呼び出されないように解析されます。

関連する問題