2011-06-16 17 views
5

何らかの方法で引数を操作してから引数への参照を返すように関数をオーバーロードしたいのですが、引数が変更可能でない場合、引数のコピー代わりに。 年を重ねると、ここで私が思いついたことがあります。rvalue関数のオーバーロード

using namespace std; 

string& foo(string &in) 
{ 
    in.insert(0, "hello "); 
    return in; 
} 

string foo(string &&in) 
{ 
    return move(foo(in)); 
} 

string foo(const string& in) 
{ 
    return foo(string(in)); 
} 

このコードは正しく動作するようですが、誰かがより良いやり方を考えることができるかどうか聞いてみたいと思います。もちろん、

string& foo(string &in) 
{ 
    in.insert(0, "hello "); 
    return in; 
} 

string foo(string in) 
{ 
    return move(foo(in)); 
} 

int main(void) 
{ 
    string var = "world"; 
    const string var2 = "const world"; 
    cout << foo(var) << endl; 
    cout << var << endl; 

    cout << foo(var2) << endl; 
    cout << var2 << endl; 

    cout << foo(var + " and " + var2) << endl; 
    return 0; 
} 

正しい出力が

hello world 
hello world 
hello const world 
const world 
hello hello world and const world 

である私はこれを行うことができれば、私はそれが少し滑らかな印象になります図:

は、ここでのテストプログラムですfooへのほとんどの関数呼び出しがあいまいであるため、機能しません。foo自身!しかし、何とかコンパイラに最初のものに優先順位を付けるように指示できたら...

私が言ったように、コードが正しく動作します。私が気に入らない主なものは、繰り返しの余分なコードです。もし私がそのような機能を持っていたら、それはかなり混乱し、そのほとんどは非常に繰り返します。だから私の質問の2番目の部分:誰もが自動的に2番目と3番目のfoo関数のコードを生成する方法を考えることができますか?例:

// implementation of magic_function_overload_generator 
// ??? 

string& foo(string &in); 
magic_function_overload_generator<foo>; 

string& bar(string &in); 
magic_function_overload_generator<bar>; 

// etc 
+7

これは恐ろしい音です。関数に渡す型に応じて、戻り値*とパラメータ*の結果状態は完全に異なる場合があります。それは微妙なバグを求めているだけです。なぜあなたは、その場でオブジェクトを修正したいのか、明示的に別の関数を呼び出すことによってコピーを返すのかをユーザに決めるのはどうですか? – jalf

+0

私は特に怖いとは思われませんが、あなたが正しいかもしれません。私が考えているのは、関数が可能な限り入力を変更するということです。それができない場合は...それはしませんが、それでも正しい戻り値を返します。 私はそれを使用するかもしれない種類のものは、 "punctuate"関数のようなものです。これは、途切れていない文字列を取り、それを修正します。結果をcoutに直接送るか、後でその文字列に対して他の操作をしたいかもしれません。だから時々あなたは一定の価値を渡すかもしれません、時には...よくあなたはそのアイデアを得ます。 – karadoc

+1

しかし、私の指摘は、プログラマが望むものではなく、比較的微妙な意味の詳細(引数の型constかどうか、それはrvalueかどうか)に依存します。)これは、プログラマが明示的に「現在の場所でオブジェクトを修正するのではなく、コピーを返す」という決定をしなくても、時間の経過とともに容易に変更される可能性があります。私はあなたがしようとしていることを理解していますが、それはプログラマーが簡単に作ることができる決定であり、あなたのライブラリーを間違った推測にすることは潜在的に非常に悪い結果をもたらす可能性があります。 – jalf

答えて

4

std::string foo(std::string in) 
{ 
    in.insert(0, "hello "); 
    return in; 
} 

あなたは左辺値を渡すと、入力文字列がコピーされます。値を渡すと移動します。

リターン値最適化という名前の関数が残っていると、リターンは基本的には何もしません。コンパイラがそれを決定すると、結果は移動されます(inは左辺です)。

右辺値についての良い点は、のほうがで、効率を上げるためにユーザーコードにどこに参照を入れるかについて考える必要があるということです。可動型では、値渡しは実質的に効率的です。

+0

これは基本的に私がやったことです。 Jalfと他の人たちは、私が全部を上書きしていると私に確信しました。 1つの関数は、変更可能なバージョンと不変のバージョンの両方を行う必要はありません。私は、何かクールなことをするためにrvalue参照を使用するという考えによって、過度に興奮したと思います。私は種別の参照を聞いたことがないと願っています。最初はこのソリューションを選んで半日も無駄にしないでください。 – karadoc

+0

心配しないで、クールなものはまだ起こる - フードの下で;) – fredoverflow

1

次の簡単なアプローチはどうですか?

string& foo (string &change) // this accepts mutable string 
{ 
    change = string("hello ") + change; 
    return change; 
} 

string foo (const string &unchange) // this accepts not mutable string 
{ 
    return string("hello ") + unchange; 
} 

output hereを参照してください。 iammilindの答え@と同じ静脈で

+0

このアプローチの悪い点は、関数の本体が2回書く必要があることです。この特定の例では、私が持っているものよりも優れていますが、fooが長い複雑なものであれば、これは必要なコードを2倍にします。 – karadoc

+2

@karadoc:変異していないものを簡単に書くことができます。 'int notModifying(const int&i){int ii = i; (ii)復帰する。 } ' –

0

が、サンセリフの重複:

#include <iostream> 
using namespace std; 

string foo(const string &unchange) { 
    return string("hello ") + unchange; 
} 

string& foo(string &change) { 
    return change = foo(static_cast<const string&>(foo)); 
} 

int main(int argc, char** argv) { 
    string a = "world"; 
    const string b = "immutable world"; 
    cout << foo(a) << '\n' << foo(b) << '\n'; 
    cout << foo(a) << '\n' << foo(b) << '\n'; 
} 

NB:あなたはまた、const資格を追加するには、ここconst_castを使用することができます。

2

なぜこのようなオーバーロードが発生するのですか?これらのすべてのオーバーロードは、1つのインタフェースfoo(x)を指定します。しかし、x parameterは、その種類に応じてinputまたはinput/outputのパラメータになる可能性があります。それは非常に、非常に間違いやすいです。ユーザーは、その変数が変更されないようにするために、何らかの追加作業を行う必要があります。プロダクションコードでは絶対にしないでください。

私は、このような過負荷に同意するだろう:それは一時的で、同時に、あなたは一時オブジェクトを再利用していない場合

string foo(string &&in); 
string foo(const string& in); 

入力パラメータが変更されることはありません。それはかなり妥当と思われる。

しかし、なぜこのようなオーバーロードをたくさん生成したいのですか? & &オーバーロードは最適化用です。私は非常に繊細な最適化を言うだろう。あなたは多くの場所でそれを必要としていますか?

本当にC++コードを生成したい場合、テンプレートは本当に良い選択ではありません。私はそれのためのいくつかの外部ツールを使用します。個人的には、Cogが好きです。私はすべて一緒に、ちょうど値で1つの渡す関数とリターンを書くの参照を取り除くでしょう

0

効率が心配されていない場合は、値渡しやconst参照渡しを行い、コピーを実行して完了させることができます。

しかし、あなたが効率を心配している場合、私はこのreplyの値渡しの提案が最良のアプローチだとは思わない。これは、NRVOはパラメータではなくローカル変数でしか動作しないように見えるため、余分なコピー/移動が発生すると思うからです。

A a1 = f1(x); 
Copy 
A a2 = f1(A()); 
Move 
A b1 = f2(x); 
Copy 
Move 
A b2 = f2(A()); 
Move 

A a3 = f1(f1(x)); 
Copy 
Move 
A a4 = f1(f1(A())); 
Move 
A b3 = f2(f2(x)); 
Copy 
Move 
Move 
A b4 = f2(f2(A())); 
Move 
Move 

A a5 = f1(f1(f1(x))); 
Copy 
Move 
A a6 = f1(f1(f1(A()))); 
Move 
A b5 = f2(f2(f2(x))); 
Copy 
Move 
Move 
Move 
A b6 = f2(f2(f2(A()))); 
Move 
Move 
Move 

はあなたができることがあります:次の結果が生成さ

#include <iostream> 

struct A 
{ 
    A() : i(0) {} 
    A(const A& x) : i(x.i) { std::cout << "Copy" << std::endl; } 
    A(A&& x) : i(x.i) { std::cout << "Move" << std::endl; } 
    void inc() { ++i; } 
    int i; 
}; 

A f1(const A& x2) { A x = x2; x.inc(); return x; } 
A&& f1(A&& x) { x.inc(); return std::move(x); } 

A f2(A x) { x.inc(); return std::move(x); } 

int main() 
{ 
    A x; 
    std::cout << "A a1 = f1(x);" << std::endl; 
    A a1 = f1(x); 
    std::cout << "A a2 = f1(A());" << std::endl; 
    A a2 = f1(A()); 
    std::cout << "A b1 = f2(x);" << std::endl; 
    A b1 = f2(x); 
    std::cout << "A b2 = f2(A());" << std::endl; 
    A b2 = f2(A()); 
    std::cout << std::endl; 
    std::cout << "A a3 = f1(f1(x));" << std::endl; 
    A a3 = f1(f1(x)); 
    std::cout << "A a4 = f1(f1(A()));" << std::endl; 
    A a4 = f1(f1(A())); 
    std::cout << "A b3 = f2(f2(x));" << std::endl; 
    A b3 = f2(f2(x)); 
    std::cout << "A b4 = f2(f2(A()));" << std::endl; 
    A b4 = f2(f2(A())); 
    std::cout << std::endl; 
    std::cout << "A a5 = f1(f1(f1(x)));" << std::endl; 
    A a5 = f1(f1(f1(x))); 
    std::cout << "A a6 = f1(f1(f1(A())));" << std::endl; 
    A a6 = f1(f1(f1(A()))); 
    std::cout << "A b5 = f2(f2(f2(x)));" << std::endl; 
    A b5 = f2(f2(f2(x))); 
    std::cout << "A b6 = f2(f2(f2(A())));" << std::endl; 
    A b6 = f2(f2(f2(A()))); 
} 

:私は、次のコードで示すように、C++ 0xの中に移動/コピーを避ける方法は、二重の過負荷だと思います例えば、複数のオーバーロードを書く避けるために、いくつかのテンプレートのトリックを行うには:

(const) T&を通過したとき param_return_type<T>::typeTある
template <class T> 
param_return_type<T&&>::type f3(T&& y, typename std::enable_if<...>::type* dummy = 0) 
{ 
    typedef return_t param_return_type<T&&>::type; 
    return_t x = static_cast<return_t>(y); 
    x.inc(); 
    return static_cast<return_t>(x); 
} 

、そしてT&&が渡されたときT&&std::enable_if<...>このテンプレートで特定のパラメータのみを使用する場合に使用できます。

std::remove_lvalue_referenceのように、param_return_type<T>::typeの定義を書く方法がわかりませんでした。誰かがどのように知っている場合は、私の投稿を編集/追加することを自由に感じてください。