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函数的代码吗?例如

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

我会一起摆脱引用,只编写一个按值传递和返回的函数:

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

如果你传递一个左值,输入字符串将被复制。如果你传递一个右值,它将被移动。

离开函数时,命名返回值优化可能会启动,因此返回基本上是无操作的。如果编译器决定不这样做,则结果将被移动(即使in是左值)。

右值引用的好处是您不必考虑在用户代码中何处放置引用以提高效率。对于可移动类型,按值传递实际上是最有效的。

于 2011-06-16T14:44:19.287 回答
2

整个问题是你为什么想要这样的重载?所有这些重载都指定了一个接口:foo(x)。但是 xparameter可能是inputinput/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

如果您不担心效率,您可以按值传递或通过 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 回答
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 回答