10

別の質問への回答として、次のコードを投稿したかった(つまり、このアイデアに基づいたコードを投稿したかった)。

#include <iostream>
#include <utility>      // std::is_same, std::enable_if
using namespace std;

template< class Type >
struct Boxed
{
    Type value;

    template< class Arg >
    Boxed(
        Arg const& v,
        typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
        )
        : value( v )
    {
        wcout << "Generic!" << endl;
    }

    Boxed( Type&& v ): value( move( v ) )
    {
        wcout << "Rvalue!" << endl;
    }
};

void function( Boxed< int > v ) {}

int main()
{
    int i = 5;
    function( i );  //<- this is acceptable

    char c = 'a';
    function( c );  //<- I would NOT like this to compile
}

ただし、MSVC 11.0は最後の呼び出しでチョークしますが、IHMOの場合と同様に、MinGW g ++ 4.7.1はそれを受け入れ、右辺値参照の仮引数を使用してコンストラクターを呼び出します。

左辺値が右辺値参照にバインドされているように見えます。glibの答えは、左辺値が右辺値に変換されるというものである可能性があります。しかし、問題は、これはコンパイラのバグであり、そうでない場合、HolyStandardはこれをどのように許可するのでしょうか。


編集:私はそれをすべて次のかなり短い例に減らすことができました:

void foo( double&& ) {}

int main()
{
    char ch = '!';
    foo( ch );
}

MSVC 11.0でコンパイルできませんが、MinGW 4.7.1でコンパイルしますか?

4

3 に答える 3

2

N3290(C ++ 11標準と同じ)には、左辺値double&&から生成された右辺値へのバインドの非規範的な例と、 §8.5.3intの更新された文言が含まれていることを発見しました。

「T1がT2に参照関連していて、参照が右辺値参照である場合、初期化式は左辺値であってはなりません。」

ルールは、非効率的な余分なコピーを回避するように設計されたと報告されています。そのようなコピーを最適化することができなかった方法はわかりませんが。とにかく、理論的根拠が合理的であるかどうかにかかわらず–そしてそれは確かに合理的な効果とは思えません!–次のコードが許可され、MSVC11とMinGWg++4.7の両方でコンパイルされます。

struct Foo {};
struct Bar { Bar( Foo ) {} };

void ugh( Bar&& ) {}

int main()
{
    Foo o;
    ugh( o );
}

したがって、MSVC 11は、左辺値->右辺値の変換を許可しないという点で間違っているようです。


編集:この問題に関する欠陥レポート、 DR1414があることを知りました。2012年2月の結論は、現在の動作仕様は、おそらく意図をどの程度反映しているかに関して「正しい」というものでした。しかし、おそらく意図の実用性に関して、それはまだ委員会で議論されていると報告されています。

于 2012-10-14T11:54:43.583 に答える
2

charスペックは確認していませんが、自動的ににキャストできると思いますint。何も(r値ではなく)割り当てることができないため、R値をタイプの一時変数int(値をより明確にするため(int)c)に渡します。

于 2012-10-14T10:16:40.123 に答える
0

おそらくあなたはこれが有効であることに同意しますか?

void foo( double ) {}  // pass-by-value

int main()
{
    char ch = '!';
    foo( ch );
}

charからへの暗黙の変換がdoubleあるため、関数は実行可能です。

編集した質問の例でも同じです。一時(つまり右辺値)を生成する暗黙の変換があり、右辺値参照引数はその一時値にバインドされます。必要に応じて、その変換を明示的にすることができます。

void foo( double&& ) {}  // pass-by-reference

int main()
{
    char ch = '!';
    foo( double(ch) );
}

しかし、この場合、それは実際には何も変わりません。これは、double->charを明示的にのみ変換できる場合(たとえば、明示的なコンストラクターまたは明示的な変換演算子を使用するクラスタイプの場合)、doubletocharが有効な暗黙的な変換である場合に必要になります。

あなたが考えている「右辺値参照は左辺値にバインドできません」ルールは、を左辺値にバインドすることを指します。T&&このルールは、にバインドされないTために破られることはなく、によって作成された一時的なものにバインドされます。暗黙の変換。 double&&char

このルールは、不要な余分なコピーを防ぐためだけでなく、以前のルールに存在していた実際の安全上の問題を修正するためにも存在します。http://www.open-std.org/JTC1/SC22/WG21/docs/papers/を参照してください。 2008 / n2812.html

編集:この行動が委員会のリフレクターで望ましいかどうかを尋ねられ(DR 1414を参照)、そうだと判断されました。この行動は意図されたものであり、正しいものです。その位置に到達するために使用された議論の1つは、このコードが現在のルールでより効率的であるというものでした。

std::vector<std::string> v;
v.push_back("text");

現在のルールでは、一時的なstd::string変換によって一時的なものが作成されてから std::vector<T>::push_back(T&&)呼び出され、一時的なものがベクターに移動されます。そのpush_backオーバーロードが変換の結果に対して実行可能でなかった場合、上記のコードが呼び出され、コピーstd::vector<T>::push_back(const T&)が発生します。現在のルールにより、この実際のユースケースはより効率的になります。rvalue-refsが暗黙的な変換の結果にバインドできないとルールが述べている場合は、移動の効率を上げるために上記のコードを変更する必要があります。

v.push_back( std::string{"text"} );

私見では、std::stringコンストラクターが明示的でない場合に明示的にコンストラクターを作成する必要はありません。push_back明示的/暗黙的なコンストラクターからの一貫した動作が必要であり、最初の例をより効率的にしたいと思います。

于 2012-10-14T15:45:17.433 に答える