6

関数をオーバーロードして、何らかの方法で引数を操作してから引数への参照を返すようにしたいのですが、引数が変更可能でない場合は、代わりに引数の操作されたコピーを返す必要があります。何年もの間それをいじった後、これが私が思いついたものです。

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

このコードは正しく機能しているようですが、誰かがそれを行うためのより良い方法を考えられるかどうか聞いてみたいです。

テストプログラムは次のとおりです。

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

私がこれを行うことができれば、それは少しきれいになると思います:

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

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

もちろん、へのほとんどの関数呼び出しはfooあいまいであるため、それは機能しません–呼び出しfoo自体を含みます!しかし、どういうわけかコンパイラに最初のものを優先するように指示できれば...

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

// 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
4

5 に答える 5

9

参照をすべてまとめて削除し、値を渡して返す関数を1つだけ記述します。

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

左辺値を渡すと、入力文字列がコピーされます。右辺値を渡すと、移動されます。

関数を離れるとき、名前付きの戻り値の最適化がおそらく開始されるので、戻りは基本的に何もしません。コンパイラがそれに反対することを決定した場合、結果は移動されます(in左辺値であっても)。

右辺値参照の良いところは、効率を上げるために、ユーザーコードのどこに参照を配置するかについて考える必要がないことです可動タイプの場合、値渡しは実質的に効率的です。

于 2011-06-16T14:44:19.287 に答える
2

全体的な問題は、なぜそのような過負荷が必要なのかということです。これらのオーバーロードはすべて、foo(x)という1つのインターフェースを指定します。ただし、xparameterは、そのタイプに応じてinputまたはパラメータになる場合があります。input/outputそれは非常に、非常にエラーが発生しやすいです。ユーザーは、変数が変更されないようにするために、追加の作業を行う必要があります。本番コードでは絶対に行わないでください。

私はそのような過負荷に同意します:

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

入力パラメーターが一時的でない場合は変更されず、同時に一時オブジェクトを再利用します。それはかなり合理的なようです。

しかし、なぜそのような過負荷をたくさん生成したいのですか?&&オーバーロードは最適化用です。非常にデリケートな最適化だと思います。本当にたくさんの場所で必要ですか?

とにかく、本当にC ++コードを生成したいのであれば、テンプレートはあまり良い選択ではありません。私はそれのためにいくつかの外部ツールを使用します。個人的には、 Cogが好きです。

于 2011-06-16T09:12:19.357 に答える
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;
}

ここに出力があります

于 2011-06-16T08:14:56.080 に答える
0

@iammilindの答えと同じですが、重複はありません。

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

注意:ここで資格const_castを追加することもできます。const

于 2011-06-16T08:34:23.857 に答える
0

効率を気にしない場合は、値渡しまたはconst参照渡しを実行し、コピーを実行してそれを実行できます。

ただし、効率が心配な場合は、この返信での値渡しの提案が最善のアプローチではないと思います。これは、NRVOはパラメータではなくローカル変数でのみ機能するように見えるため、余分なコピー/移動が発生すると思うためです。次のコードに示すように、C ++ 0xで移動/コピーを回避する方法は、デュアルオーバーロードだと思います。

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

これにより、次の結果が得られます。

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

たとえば、次のように、複数のオーバーロードの記述を回避するために、いくつかのテンプレートトリックを実行できる場合があります。

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

渡されたとき、および渡されたときはどこにparam_return_type<T>::typeありますか。このテンプレートで特定のパラメータのみを取得する場合に使用できます。T(const) T&T&&T&&std::enable_if<...>

param_return_type<T>::typeの定義がないように見えるので、の定義の書き方がわかりませんでしstd::remove_lvalue_referenceた。誰かがその方法を知っているなら、私の投稿を編集/追加してください。

于 2011-06-17T05:05:45.723 に答える