右值函数重载
rvalue function overloading
我想重载一个函数,以便它以某种方式操作其参数,然后返回对该参数的引用 - 但如果参数不可变,那么它应该返回参数的操纵副本。在弄了很久之后,这就是我想出的。
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
我会一起删除引用,只编写一个按值传递和返回的函数:
std::string foo(std::string in)
{
in.insert(0, "hello ");
return in;
}
如果传递左值,则将复制输入字符串。如果传递右值,它将被移动。
当离开函数时,命名的返回值优化可能会启动,因此返回基本上是无操作的。如果编译器决定不这样做,结果将被移动(即使in
是一个左值(。
右值引用的好处是,您必须少考虑在用户代码中放置引用的位置以提高效率。对于可移动类型,按值传递实际上与它一样高效。
整个问题是你为什么要有这样的重载?所有这些重载都指定了一个接口:foo(x(。但是 x parameter
可能是input
或input/output
参数,具体取决于其类型。它非常非常容易出错。用户应该做一些额外的工作来确保它的变量不会被改变。切勿在生产代码中执行此操作。
我同意这样的重载:
string foo(string &&in);
string foo(const string& in);
如果输入参数不是临时对象,则永远不会更改该参数,同时重复使用临时对象。这似乎很合理。
但是,为什么要生成大量这样的重载?&&&重载是为了优化。我会说非常微妙的优化。您确定在很多地方都需要它吗?
无论如何,如果你真的想生成C++代码,模板不是一个非常好的选择。我会为此使用一些外部工具。就个人而言,我更喜欢齿轮。
遵循简单的方法怎么样?
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;
}
在此处查看其输出。
与@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
资格。
如果你不担心效率,你可以按值传递或按常量引用传递,然后做一个复制并完成它。
但是,如果您担心效率,我认为此回复中的传递值建议不是最好的方法。这是因为我认为它会导致额外的副本/移动,因为 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
在传递(const) T&
时T
,在传递T&&
时T&&
。 std::enable_if<...>
,如果您只希望此模板采用特定参数,则可以使用。
我不知道如何写param_return_type<T>::type
的定义,因为似乎没有std::remove_lvalue_reference
。如果有人知道如何,请随时编辑/添加到我的帖子中。
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 可以打印矢量和矢量中的矢量的非重载C++函数
- 错误 没有与参数列表匹配的重载函数"getline"实例
- 使用模板重载函数
- C++线程中,没有重载函数接受 X 参数
- std::vector 没有重载函数的实例与参数列表匹配
- C++重载函数,一个采用基类的参数,另一个采用派生类的参数
- 错误:无法解析对重载函数的引用;你的意思是调用它吗?
- 对重载函数find_first_not_of的不明确调用
- 如何从重载解析中删除重载函数?
- CUDA:重载函数"isnan"的多个实例
- C++派生类重载函数(带有 std::function 参数)不可见
- 避免在人为的重载函数调用中拼写出类型
- C++:如何为多个重载函数保留通用代码路径?
- 什么时候可以使用常量装饰调用我的重载函数?
- 尝试使用谓词函数会导致错误:"std::sort"未找到匹配的重载函数
- std::调用,未找到匹配的重载函数
- 为什么在传递长整型时调用具有两个双精度类型的参数的重载函数?
- 为什么使用不匹配的参数调用重载函数仍然有效
- 如何通过签名作为模板参数来解决重载函数?