7

次の例でFooは、は意図したことを実行していませんが、なぜこれがコンパイルを許可されているのか理解できません。

#include <string>
#include <iostream>

typedef std::string& T;

T Foo(int & i)
{
    return T(i);
}

int main()
{
    int a = 1;
    std::string & s = Foo(a);
}

私はこれをテンプレートで発見しましたが、それがテンプレートtypedefとは無関係であることを示しています。言うまでもなく、sここでは有効な文字列ではありません。Fooの戻り値で値を作成すると、コンパイルエラーが発生すると思います。

ここで何が欠けていますか?

4

2 に答える 2

6

まず第一に、このコードはうまくコンパイルされるので、問題が実際にテンプレートと関係がないことは何の価値もありません。

typedef std::string& T;
T Foo(int& i) {
    return T(i);
}

これがコンパイルされると思う理由は、returnステートメントがと同等であるためです

return reinterpret_cast<T>(i);

Tたまたま参照メンバーである場合に備えて。...そしてもちろんこれはコンパイルします:あなたは自分が何をしているのかを知っていると約束し、コンパイラに親切にあなたを信じるように頼みました。

OK、5.2.3 [expr.type.conv]段落1で見つけました:

...式リストが単一の式である場合、型変換式は、対応するキャスト式(5.4)と同等です(定義において、および意味で定義されている場合)。..。

...および5.4[expr.cast]段落4:

[他の形式のキャスト]reinterpret_cast(5.2.10)[...]によって実行される変換は、明示的な型変換のキャスト表記を使用して実行できます。[...]

(エリジオンは、ユーザー定義型、組み込み型変換、const変換などを含む場合をカバーします。)

于 2012-12-17T00:56:59.230 に答える
5

これはテンプレートとは何の関係もありません。推定されたテンプレートパラメータではなく、T単なるtypedefの場合、同じ結果が得られます。std::string&

#include <string>

typedef std::string& T;

T Foo(int & i)
{
    return T(i);
}

int main()
{
    int a = 1;
    std::string & s = Foo(a);
}

ディートマーの答えは、これをさらに単純化して次のようにできることに気づきました。

#include <string>

typedef std::string& T;

int main()
{
    int a = 1;
    std::string & s = T(a);
}

ここT(a)で、はキャストと同じです。(T)aつまり(std::string&)a、(5.4 [expr.cast]のルールに従って)const_castそれが有効な場合(そうではない)、またはstatic_cast有効な場合(そうでない場合)、またはstatic_castフォローされている場合に実行しますconst_castそれが有効である場合(そうでない場合)、またはそれが有効である場合reinterpret_cast(それが有効である場合)、またはそれにreinterpret_cast続くconst_cast場合、それが有効である場合、式は不正な形式になります。

つまり、Dietmarが言ったように、それはreinterpret_cast、つまり、

std::string & s = reinterpret_cast<std::string&>(a);

元のコードがコンパイルされるのは非常に驚くべきことですが、上記の行と同じであるため、コンパイルが許可されています。ただし、キャストの結果を使用することは未定義の動作です。

T(a)がキャストと同等であるという驚きを避けるために、新しいC ++ 11の統一初期化構文を使用しますT{a}。これは常に初期化であり、キャスト式ではありません。

すばらしい質問です。調査して回答したところ、新しい知識を提供してくれたJaredCとDietmarのおかげで、以前は気づかなかった新しい落とし穴がわかりました。

于 2012-12-17T00:58:32.723 に答える