функция "id" в C ++ 0x - PullRequest
       9

функция "id" в C ++ 0x

7 голосов
/ 23 августа 2011

Чтение этот ответ о возврате rvalue-ссылок из функции заставил меня задуматься, как мне написать 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 имеет наблюдаемый эффект.

Как мне написать id функцию в C ++ 0x, которая, в частности, работает для типов без конструкторов перемещения / копирования.

Ответы [ 3 ]

7 голосов
/ 23 августа 2011

Вы не можете.Как правило, вы не должны писать функции, которые возвращают rvalue-ссылки, и, как вы правильно указали, вы не можете продлить время жизни временного достаточно долго.

0 голосов
/ 14 июля 2012

Большинство вещей в языках программирования не являются полностью и полностью бесплатными. Если вы не пишете код только во время компиляции, написание функции идентификации вряд ли будет бесплатным.

Давайте немного переделаем ваш код:

#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 (), а значение является lvalue (содержит значение r), оно не было автоматически перемещено при возврате и, следовательно, было скопировано.

Случай C вызывает id2(), который вызывает конструктор перемещения по возвращении.

Случаи D и E аналогичны случаям B и C соответственно, за исключением того, что auto не используется, если вы скептически относились к этому.

Ходы следует рассматривать как оптимизированные копии и как плохие копии в худшем случае (хотя они часто будут намного лучше). Даже оптимальное перемещение имеет стоимость (например, копирование некоторых данных (обычно) из одного стекового фрейма в другой). Единственный способ полностью избежать копирования / перемещения в коде времени выполнения - это когда компилятор может использовать оптимизацию возвращаемого значения и копирование ellison.

0 голосов
/ 23 августа 2011

То, что вы хотите сделать, называется совершенная пересылка , и в 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"));
...