n 個のパラメーターを持つコンストラクターがある場合、その引数は右辺値と左辺値にすることができます。可能な右辺値/左辺値の組み合わせごとに 2^n コンストラクターを記述せずに、右辺値の移動セマンティクスでこれをサポートすることは可能ですか?
3 に答える
次のように、それぞれを値で取得します。
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;
};
引数による関数パラメーターの初期化は、コピー コンストラクターまたはムーブ コンストラクターのいずれかになります。そこから、関数パラメーター値をメンバー変数に移動するだけです。
覚えておいてください: copy- および move-semantics は、クラスによって提供されるサービスであり、あなたによって提供されるものではありません。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;
};
次のコードideoneリンクを取ります。
#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
ます。このようにして、コピーと移動の場合に別々のコードを効果的に使用できるため、必要なコピーは1つまたは移動は1つだけです。値渡しアプローチでは、少なくとも2つの移動/コピーが必要です。ただし、コンパイラが引数の代わりに値を作成できるprvalueの場合は、1つの移動のみが必要です。
使用している 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