Передача / перемещение параметров конструктора в C ++ 0x - PullRequest
24 голосов
/ 14 июля 2011

Если у меня есть конструктор с n параметрами, так что любой аргумент этого может быть rvalue и lvalue.Можно ли сделать это с помощью семантики перемещения для значений r, не записывая 2 ^ n конструкторов для каждой возможной комбинации значений rvalue / lvalue?

Ответы [ 3 ]

30 голосов
/ 14 июля 2011

Вы берете каждый из них по значению, например так:

struct foo
{
    foo(std::string s, bar b, qux q) :
    mS(std::move(s)),
    mB(std::move(b)),
    mQ(std::move(q))
    {}

    std::string mS;
    bar mB;
    qux mQ;
};

Инициализация параметров функции аргументом будет либо конструктором копирования, либо конструктором перемещения.Оттуда вы просто перемещаете значения параметров функции в переменные-члены.

Помните: семантика копирования и перемещения - это служба, предоставляемая классом, а не вами .В C ++ 0x вам больше не нужно беспокоиться о том, как получить собственную «копию» данных;просто спросите об этом и позвольте классу сделать это:

foo f("temporary string is never copied", bar(), quz()); // no copies, only moves
foo ff(f.mS, f.mB, f.mQ); // copies needed, will copy
foo fff("another temp", f.mB, f.mQ); // move string, copy others

Примечание: ваш конструктор принимает только значения, эти значения сами поймут, как создать себя.Оттуда, конечно, вам нужно переместить их туда, куда вы хотите.

Это применимо везде.Есть функция, которая нуждается в копии?Сделайте это в списке параметров:

void mutates_copy(std::string s)
{
    s[0] = 'A'; // modify copy
}

mutates_copy("no copies, only moves!");

std::string myValue = "don't modify me";
mutates_copy(myValue); // makes copy as needed
mutates_copy(std::move(myValue)); // move it, i'm done with it

В C ++ 03 вы могли бы эмулировать его довольно хорошо, но это не было обычным (по моему опыту):

struct foo
{
    foo(std::string s, bar b, qux q)
    // have to pay for default construction
    {
        using std::swap; // swaps should be cheap in any sane program

        swap(s, mS); // this is effectively what
        swap(b, mB); // move-constructors do now,
        swap(q, mQ); // so a reasonable emulation
    }

    std::string mS;
    bar mB;
    qux mQ;
};
1 голос
/ 14 июля 2011

Возьмите следующий код ideone link .

#include <iostream>

class A
{
public:
  A() : i(0) {}
  A(const A& a) : i(a.i) { std::cout << "Copy A" << std::endl; }
  A(A&& a) : i(a.i) { std::cout << "Move A" << std::endl; }
  int i;
};

template <class T>
class B1
{
public:
  template <class T1, class T2>
  B1(T1&& x1_, T2&& x2_) : x1(std::forward<T1>(x1_)), x2(std::forward<T2>(x2_)) {}
  B1(const B1<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B1" << std::endl; }
  B1(B1<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B1" << std::endl; }
private:
  T x1;
  T x2;
};

template <class T>
class B2
{
public:
  B2(T x1_, T x2_) : x1(std::move(x1_)), x2(std::move(x2_)) {}
  B2(const B2<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B2" << std::endl; }
  B2(B2<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B2" << std::endl; }
private:
  T x1;
  T x2;
};

A&& inc_a(A&& a) { ++a.i; return static_cast<A&&>(a); }
A inc_a(const A& a) { A a1 = a; ++a1.i; return a1; }

int main()
{
  A a1;
  A a2;
  std::cout << "1" << std::endl;
  B1<A> b1(a1,a2);
  std::cout << "2" << std::endl;
  B1<A> b2(a1,A());
  std::cout << "3" << std::endl;
  B1<A> b3(A(),a2);
  std::cout << "4" << std::endl;
  B1<A> b4(A(),A());
  std::cout << "5" << std::endl;
  B2<A> b5(a1,a2);
  std::cout << "6" << std::endl;
  B2<A> b6(a1,A());
  std::cout << "7" << std::endl;
  B2<A> b7(A(),a2);
  std::cout << "8" << std::endl;
  B2<A> b8(A(),A());
  std::cout << "9" << std::endl;
  std::cout << std::endl;
  std::cout << "11" << std::endl;
  B1<A> b11(a1,a2);
  std::cout << "12" << std::endl;
  B1<A> b12(a1,inc_a(A()));
  std::cout << "13" << std::endl;
  B1<A> b13(inc_a(A()),a2);
  std::cout << "14" << std::endl;
  B1<A> b14(inc_a(A()),inc_a(A()));
  std::cout << "15" << std::endl;
  B2<A> b15(a1,a2);
  std::cout << "16" << std::endl;
  B2<A> b16(a1,inc_a(A()));
  std::cout << "17" << std::endl;
  B2<A> b17(inc_a(A()),a2);
  std::cout << "18" << std::endl;
  B2<A> b18(inc_a(A()),inc_a(A()));
  std::cout << "19" << std::endl;
}

, который выводит следующее:

1
Copy A
Copy A
2
Copy A
Move A
3
Move A
Copy A
4
5
Copy A
Copy A
Move A
Move A
6
Copy A
Move A
Move A
7
Copy A
Move A
Move A
8
9

11
Copy A
Copy A
12
Copy A
Move A
13
Move A
Copy A
14
Move A
Move A
15
Copy A
Copy A
Move A
Move A
16
Move A
Copy A
Move A
Move A
17
Copy A
Move A
Move A
Move A
18
Move A
Move A
Move A
Move A
19

Как можно видеть, передача по значению вB2 вызывает дополнительное перемещение для каждого аргумента во всех случаях, кроме случаев, когда аргумент является prvalue.

Если вам нужна лучшая производительность, я предлагаю шаблонный подход в B1.Таким образом, у вас фактически есть отдельный код для случаев копирования и перемещения, и, следовательно, требуется только одна копия или одно перемещение.При подходе передачи по значению требуются как минимум два перемещения / копии, за исключением случая предварительного значения, когда компилятор может создать значение вместо аргумента, когда требуется только одно перемещение.

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

В зависимости от того, какой компилятор c ++ вы используете, вы можете посмотреть на «функции с переменными списками аргументов»

Идея состоит в том, что вы можете передать в метод столько параметров, сколько захотите, и он просто заполняетсяв массив, который можно перебрать.

Для Microsoft C ++ могут быть полезны следующие записи блога:

http://msdn.microsoft.com/en-us/library/fxhdxye9(v=VS.100).aspx http://blogs.msdn.com/b/slippman/archive/2004/02/16/73932.aspx

...