3

関数があり、引数に(カプセル化された)address_ofを返します。は左辺値と右辺値の両方で動作する必要があるため、 には 2 つのバージョンがあります。1 つは参照を受け入れ、もう 1 つは右辺値参照を受け入れます。テンポラリーのアドレスを取得することは悪いことなので、 の右辺値バージョンは、が実際に何かを所有するためにムーブ コンストラクトを実行する必要があります。実装は簡単です:Pointershared_ptraddress_ofaddress_ofaddress_ofPointer

template<class T>
inline Pointer address_of(T& value) {
    return Pointer(&value);
}

template<class T>
inline Pointer address_of(T&& value) {
    return Pointer(new T(std::move(value)));
}

そして、期待どおりに一時的な作品のアドレスを取得します。

Pointer p = address_of(Derived());

しかし、次のコードでテストすると:

Base* object = new Derived();
Pointer p = address_of(*object);

GCC は、への呼び出しaddress_ofがあいまいであると文句を言います。

error: call of overloaded ‘address_of(Base&)’ is ambiguous
note: candidates are: Pointer address_of(T&) [with T = Base]
note:                 Pointer address_of(T&&) [with T = Base&]

単項*は常に左辺値を返すという印象を受けました。その場合、右辺値のバージョンは考慮されるべきではありません。ここで何が起こっているのですか?

4

1 に答える 1

3

問題は参照の減衰によって引き起こされます: (正しい用語は「参照の崩壊」です)

template < typename T >
void f(T && t) { .... }

int x; f(x); // what is f()?

コード内の質問に対する答えは、f() は次のとおりです。

void f(T& && t) { .... }

参照の減衰により、これは次のようになります。

void f(T& t) { .... }

ご想像のとおり、これはもちろん、次のように定義されたものとあいまいになります。

template < typename T >
void f(T & t) { .... }

これはうまくいくかもしれません(修正版):

#include <type_traits>
#include <utility>

template < typename T >
struct pointer
{
  pointer(T& t) {}
  pointer(T&& t) {}
};

template < typename T >
pointer<typename std::remove_reference<T>::type> 
address_of(T && t)
{ 
  return pointer<typename std::remove_reference<T>::type>(std::forward<T>(t));
}

int main()
{
  int x = 5;
  pointer<int> o = address_of(x);
  pointer<int> p = address_of(5);
}

この参照の減衰は、T でテンプレート化された関数でのみ発生するためです。この場合、ポインター クラスはそうですが、コンストラクターは実際にはテンプレート自体ではないため、T& は T&& バージョンの有効な T ではありません。

address_of はポインターのテンプレート パラメーターとして T を使用しているだけだったため、最初のバージョンでもコードと同じ問題がありました。実際には生の型が必要です。

于 2010-07-02T16:37:53.780 に答える