8

ここで作成されたGMan のおいしく邪悪なauto_castユーティリティ関数を考えると、(MSVC 10.0 で) 右辺値から取得しようとしているときにコンパイルされない理由を理解しようとしています。auto_cast

私が使用しているコードは次のとおりです。

template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
  public:
    template <typename R>
    friend auto_cast_wrapper<R> auto_cast(R&& pX);

    template <typename U>
    operator U() const
    {
      return static_cast<U>( std::forward<T>(mX) );
    }

  private:
    //error C2440: 'initializing': cannot convert from 'float' to 'float &&'
    auto_cast_wrapper(T&& pX) : mX(pX) { }

    T&& mX;
};

template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
  return auto_cast_wrapper<R>( std::forward<R>(pX) );
}

int main()
{
  int c = auto_cast( 5.0f );  // from an rvalue
}

私の能力を最大限に発揮するために、C++0x 参照の折りたたみ規則と、ここで概説されているテンプレート引数推定規則に従おうとしましたが、上記のコードが機能するはずであると言える限りです。

0x より前の C++ では、参照への参照を取ることは許可されていないことを思い出してください。A& & のようなものはコンパイル エラーを引き起こします。対照的に、C++0x では、次の参照折りたたみ規則が導入されています。

  • A& & が A& になる
  • A& && は A& になります
  • A&& & が A& になる
  • A&& && は A&& になります

2 番目の規則は、テンプレート引数への右辺値参照によって引数を取る関数テンプレートの特別なテンプレート引数推定規則です。

template<typename T>  
void foo(T&&);

ここでは、次の規則が適用されます。

  1. 型 A の左辺値で foo が呼び出されると、T は A& に解決されるため、上記の参照折りたたみルールにより、引数の型は事実上 A& になります。
  2. 型 A の右辺値で foo が呼び出されると、T は A に解決されるため、引数の型は A&& になります。

への呼び出しにマウスをauto_cast( 5.0f )合わせると、ツールチップにその戻り値が として正しく表示されるようになりましauto_cast_wrapper<float>た。これは、コンパイラが規則 2 に正しく従っていることを意味します。

型 A の右辺値で foo が呼び出されると、T は A に解決されます。

したがって、 があるauto_cast_wrapper<float>ので、コンストラクターはインスタンス化して を取得する必要がありますfloat&&floatしかし、エラーメッセージは、インスタンス化してby 値を取ることを暗示しているようです。

ツールチップの表示エラー

T=float が正しく、T&& パラメータが T? になることを示す完全なエラー メッセージを次に示します。

 main.cpp(17): error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
     You cannot bind an lvalue to an rvalue reference
     main.cpp(17) : while compiling class template member function 'auto_cast_wrapper<T>::auto_cast_wrapper(T &&)'
     with
     [
         T=float
     ]
     main.cpp(33) : see reference to class template instantiation 'auto_cast_wrapper<T>' being compiled
     with
     [
         T=float
     ]

何かご意見は?

4

3 に答える 3

4

T&& 引数を auto_cast_wrapper コンストラクターに std::forward するのを忘れました。これにより、転送チェーンが中断されます。コンパイラは警告を出すようになりましたが、問題なく動作しているようです。

template <typename T>
class auto_cast_wrapper
{
  public:
    template <typename R>
    friend auto_cast_wrapper<R> auto_cast(R&& pX);

    template <typename U>
    operator U() const
    {
      return static_cast<U>( std::forward<T>(mX) );
    }

  private:
    //error C2440: 'initializing': cannot convert from 'float' to 'float &&'
    auto_cast_wrapper(T&& pX) : mX(std::forward<T>(pX)) { }

    auto_cast_wrapper(const auto_cast_wrapper&);
    auto_cast_wrapper& operator=(const auto_cast_wrapper&);

    T&& mX;
};

template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
  return auto_cast_wrapper<R>( std::forward<R>(pX) );
}

float func() {
    return 5.0f;
}

int main()
{

  int c = auto_cast( func() );  // from an rvalue
  int cvar = auto_cast( 5.0f );

  std::cout << c << "\n" << cvar << "\n";
  std::cin.get();
}

5 のペアを出力します。

于 2010-10-27T14:11:57.673 に答える
2

テストされていないコードを投稿して申し訳ありません。:)

引数も転送する必要があるという DeadMG は正しいです。警告は誤りで、MSVC にはバグがあると思います。呼び出しから検討してください:

auto_cast(T()); // where T is some type

T()つまり、auto_cast関数、auto_cast_wrapperのコンストラクター、およびユーザー定義の変換はすべて、まだ有効なオブジェクトを参照しています。

(ラッパーは変換または破棄以外何もできないため、渡された値より長く存続することはできませんauto_cast。)

メンバーをただのT. ただし、元のオブジェクトを直接キャストするのではなく、コピー/移動を行います。しかし、おそらくコンパイラの最適化により、それはなくなります。


いいえ、転送は不要ではありません。自動的に変換する値のカテゴリを維持します。

struct foo
{
    foo(int&) { /* lvalue */ }
    foo(int&&) { /* rvalue */ }
};

int x = 5;
foo f = auto_cast(x); // lvalue
foo g = auto_cast(7); // rvalue

そして、私が間違っていなければ、変換演算子はマークされるべきではありません (確かにそうである必要はありません) const

于 2010-10-27T18:46:27.607 に答える
2

コンパイルされない理由は、これがコンパイルされない理由と同じです。

float rvalue() { return 5.0f }

float&& a = rvalue();
float&& b = a; // error C2440: 'initializing' : cannot convert from 'float' to 'float &&'  

それa自体が左辺値であるため、バインドすることはできませんbauto_cast_wrapperコンストラクターでは、これを修正するために on 引数を再度使用する必要がありましstd::forward<T>た。上記の特定の例でのみ使用できますstd::move(a)が、これは左辺値でも機能する汎用コードをカバーしていないことに注意してください。したがって、auto_cast_wrapperコンストラクターは次のようになります。

template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
  public:
    ...

  private:
    auto_cast_wrapper(T&& pX) : mX( std::forward<T>(pX) ) { }

    T&& mX;
};

残念ながら、これは現在、未定義の動作を示しているようです。次の警告が表示されます。

警告 C4413: 'auto_cast_wrapper::mX': 参照メンバーは、コンストラクターの終了後に保持されない一時に初期化されます

変換演算子が起動される前に、リテラルが範囲外になったようです。これは MSVC 10.0 の単なるコンパイラのバグかもしれませんが。GMan's answerから、一時的なライフタイムは完全な式の終わりまで存続する必要があります。

于 2010-10-27T20:09:52.307 に答える