簡體   English   中英

C ++ 0x中的“id”函數

[英]“id” function in C++0x

閱讀這個關於從函數返回右值引用的回答了我的思維,我怎么能寫一個id中的C ++ 0x功能。

基本上,我希望id是一個什么都不做的函數,一個對程序沒有可觀察影響的函數。

我的第一次嘗試如下:

#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;
}

但是,我擔心在這種情況下,x2是懸空參考,因為X("x2")在“第2行”執行之前被銷毀。

所以在這里,很明顯id具有可觀察到的效果。

如何在C ++ 0x中編寫id函數,特別適用於沒有移動/復制構造函數的類型。

你不能。 通常,您不應該編寫返回rvalue引用的函數 - 正如您正確指出的那樣,您不能將臨時的生命周期延長到足夠長的時間。

編程語言中的大多數東西都不是完全免費的。 除非您只編寫編譯時代碼,否則編寫身份函數不太可能是免費的。

讓我們稍微修改你的代碼:

#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的=在這種情況下相當於的右手側傳遞=到X,即,它不是一個分配。

情況B調用id1() ,它不移動其返回參數。 由於返回的值沒有在id()的調用堆棧上定義,並且值是左值(保持rvalue),因此在返回時不會自動移動,因此被復制。

情況C調用id2() ,它返回時調用移動構造函數。

案例D和E分別與案例B和案例C相同,但如果您對此案持懷疑態度,則不使用auto案例。

移動應被視為優化副本,並且在最壞的情況下與副本一樣糟糕(盡管它們通常會好得多)。 即使是最佳移動也會產生成本(例如,將一些數據(通常)從一個堆棧幀復制到另一個堆棧幀)。 在運行時代碼中完全避免復制/移動的唯一方法是返回值優化和復制ellison符合編譯器使用的條件。

你想要做的是完美轉發 ,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"));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM