2011-08-23 11 views
7

Read this関数から返された右辺値の参照に関する答えは私には分かりました。どうすればid関数をC++ 0xに書くことができますか?C++の "id"関数0x

基本的には、私はidをdo nothing関数、つまりプログラムに観察可能な影響を与えない関数にしたいと考えています。以下れる

私の最初の試み:

#include <iostream> 

class X 
{ 
public: 
    X(std::string&& s) : s(std::move(s)) {}; 
    X(const std::string& s) : s(s) {}; 
    std::string s; 
    ~X() { std::cout << "Destroying: " << s << std::endl; } 
private: 
    X(const X&) {}; 
    X(X&&) {}; 
}; 

template <class T> 
T&& id(T&& x) { return static_cast<T&&>(x); } 

int main() 
{ 
    auto&& x1 = X("x1"); 
    std::cout << "Line 1" << std::endl; 
    auto&& x2 = id(X("x2")); 
    std::cout << "Line 2" << std::endl; 
} 

はしかし、私はX("x2")は「ライン2」を実行する前に破壊されたとして、この場合には、x2は、ダングリング参照であることを恐れています。

ここでは、かなり明確にidが観察可能な効果を持っています。

id関数をC++ 0xに書くにはどうすればよいですか?特に、ムーブ/コピーコンストラクタのないタイプで有効です。

+0

問題は、返された参照が引き続き渡された同じオブジェクトを参照しているかどうかを関数宣言が伝えないことです。コンパイラが関数の実装をチェックして、それが何を返すのかを判断し、この分析に依存して一時的なライフタイムを作るように強制するのは妥当ではありません。 – sellibitze

答えて

7

できません。原則として、rvalue参照を返す関数を記述しないでください。正しく指摘したように、一時的な存続期間を十分に長くすることはできません。

0

何がやりたいことは完璧転送と呼ばれ、それをしないSTL関数が存在している。

template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept 
{ 
    return static_cast<T&&>(t) 
} 
template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept 
{ 
    return static_cast<T&&>(t) 
} 

あなたは参照崩壊を避けるためにremove_referenceを必要とするが。そして、それを使用するときは、転送しようとしているオブジェクトのタイプを指定する必要があります:

std::forward<X>(X("x2")); 
+1

私はそれが古い仕様であり、現在の標準ではないと信じています。さらに、問題を解決しません。 – Puppy

0

プログラミング言語のほとんどのものは完全に完全に無料ではありません。コンパイル時のみのコードを書くのでない限り、アイデンティティ関数を書くことは自由ではないでしょう。

#include <algorithm> 
#include <iostream> 

template <typename T> 
T id1(T&& t) 
{ 
    return t; 
} 

template <typename T> 
T id2(T&& t) 
{ 
    return std::move(t); 
} 


class X 
{ 
public: 
    X() 
    { output0("Xdef"); } 
    X(std::string const& s) : label_(s) 
    { output1("Xstr",s); } 
    X(X const& x) : label_(x.label_) 
    { output1("Xcopy", x); } 
    X(X&& x) : label_(std::move(x.label_)) 
    { output1("Xmove", x); } 

    X& operator =(X const& x) 
    { 
    output1("operator =copy", x); 
    label_ = x.label_; 
    return *this; 
    } 

    X& operator =(X&& x) 
    { 
    using std::swap; 
    output1("operator =move", x); 
    swap(label_, x.label_); 
    return *this; 
    } 

    ~X() 
    { output0("~X"); } 

private: 
    void output_id() const 
    { 
    std::cout << this << '[' << label_ << "]"; 
    } 

    void output0(std::string const& name) const 
    { 
    output_id(); 
    std::cout << ": " << name << "()" << std::endl; 
    } 

    void output1(std::string const& name, std::string const& str) const 
    { 
    output_id(); 
    std::cout 
     << ": " << name 
     << "(\"" << str 
     << "\")" << std::endl; 
    } 

    void output1(std::string const& name, X const& arg) const 
    { 
    output_id(); 
    std::cout << ": " << name << '('; 
    arg.output_id(); 
    std::cout << ')' << std::endl; 
    } 

    std::string label_; 
}; 

int main() 
{ 
    { 
    std::cout << "CASE A:\n"; 
    auto x = X("x1"); 
    } 
    std::cout << "\n"; 
    { 
    std::cout << "CASE B:\n"; 
    auto x = id1(X("x2")); 
    } 
    std::cout << "\n"; 
    { 
    std::cout << "CASE C:\n"; 
    auto x = id2(X("x3")); 
    } 
    std::cout << "\n"; 
    { 
    std::cout << "CASE D:\n"; 
    X x = id1(X("x4")); 
    } 
    std::cout << "\n"; 
    { 
    std::cout << "CASE E:\n"; 
    X x = id2(X("x5")); 
    } 
}  

と実行したときには(GCCのv4.8のスナップショットを使用して)出力:

のは、あなたのコードビットを手直ししてみましょう

$ ./a.out 
CASE A: 
0x7fff411fc530[x1]: Xstr("x1") 
0x7fff411fc530[x1]: ~X() 

CASE B: 
0x7fff411fc540[x2]: Xstr("x2") 
0x7fff411fc520[x2]: Xcopy(0x7fff411fc540[x2]) 
0x7fff411fc540[x2]: ~X() 
0x7fff411fc520[x2]: ~X() 

CASE C: 
0x7fff411fc540[x3]: Xstr("x3") 
0x7fff411fc520[x3]: Xmove(0x7fff411fc540[]) 
0x7fff411fc540[]: ~X() 
0x7fff411fc520[x3]: ~X() 

CASE D: 
0x7fff411fc540[x4]: Xstr("x4") 
0x7fff411fc520[x4]: Xcopy(0x7fff411fc540[x4]) 
0x7fff411fc540[x4]: ~X() 
0x7fff411fc520[x4]: ~X() 

CASE E: 
0x7fff411fc540[x5]: Xstr("x5") 
0x7fff411fc520[x5]: Xmove(0x7fff411fc540[]) 
0x7fff411fc540[]: ~X() 
0x7fff411fc520[x5]: ~X() 
$ 

ケースAは、単にコンストラクタを呼び出しますこの場合の=は、=の右側をXに渡すのと同じです。つまり、代入ではありません。

ケースBは、戻り引数を移動しないid1()を呼び出します。戻り値はid()の呼び出しスタックで定義されておらず、値は左辺値(右辺値を保持)であるため、戻り時に自動的に移動せず、したがってコピーされました。

ケースCはid2()を呼び出し、返されると移動コンストラクタを呼び出します。

ケースDとEはそれぞれケースBとCと同じですが、autoが使用されていないという点を除いては同じです。

移動は最適化されたコピーと見なされますが、最悪の場合はコピーほど悪くなります(ただし、しばしばはるかに優れています)。最適な移動であってもコストがかかる(例えば、あるスタックフレームから別のスタックフレームに(通常は)データをコピーする)。実行時コードでコピー/移動が完全に回避される唯一の方法は、戻り値の最適化とコピーellisonがコンパイラの使用に適している場合です。