我试图理解currying和调用一个函数的概念,该函数连接三个字符串,但是只传递两个字符串并使用第二个参数两次。
然而,当我这样做时,第二个参数根本没有被发送到函数,它打印出一个空字符串。这是一个非常明显的错误吗?
string concatthreestrings(string a,string b,string c){
cout<<"Value of A: "<<a<<endl;
cout<<"Value of B: "<<b<<endl;
cout<<"Value of C: "<<c<<endl;
return a+b+c;
}
int main()
{
typedef std::function< string( string,string) > fun_t ;
using namespace std::placeholders;
fun_t fn = std::bind( concatthreestrings, _1, _2, _2);
cout<<endl<<fn( "First","Second")<<endl;
}
这是给出以下输出。不使用_2两次意味着第二个参数将被传递给第二个和第三个。如果在其位置使用字符串,则其工作正常。
答案 0 :(得分:5)
复制字符串很昂贵。由于std::bind
认为占位符的值仅使用一次,因此它会对字符串执行std::move
。这是针对每个参数完成的,因此,b
或c
被移动,这意味着空字符串。
您可以通过使用const-reference传递参数来明确说出您的意思来改变该行为:
string concatthreestrings(string const& a,string const& b,string const& c)
现在,它应该有效。
答案 1 :(得分:2)
我使用这个表现出相同行为的小例子进行了一些测试:
#include <functional>
#include <iostream>
#include <string>
using std::string;
void print(string s1, string s2)
{
std::cout << s1 << s2 << '\n';
}
int main()
{
using namespace std::placeholders;
typedef std::function< void(string) > fn_t;
fn_t func = std::bind(print, _1, _1);
std::string foo("foo");
func(foo);
}
// outputs: foo
请注意,我定义了一个名为“foo”的字符串对象,而不是使用字符串文字。行为是一样的,所以问题与此无关。
我认为问题来自你的typedef。 bind
(未指定)的返回被转换为按值string
按值的函数,而bind返回的包装器可能通过rvalue-reference获取其参数,完美地转发他们。您应该使用auto
关键字,而不是使用自己的typedef,以便编译器自动推导出func
的类型。如果我们按如下方式修改main,我们将获得预期的行为:
int main()
{
using namespace std::placeholders;
auto func = std::bind(print, _1, _1);
std::string foo("foo");
func(foo);
}
// outputs: foofoo
另一个解决方案是替换你的typedef,以便func
通过引用到const获取其参数:
typedef std::function< void(string const &) > fn_t;
我真的不明白为什么其他typedef不起作用...大概是字符串移动了,正如@ipc所说,但我不知道执行的这一点发生在什么时候。我甚至不确定这是标准行为,因为function
和bind
返回的包装器都应该使用完美转发。也许GCC包含一些优化,当它们通过值传递包装器参数时会移动它?
修改的
我做了一些测试,结果发现GCC的std::function
实现对其参数执行了一次移动,而std::bind
的包装返回却没有。我仍然不知道这是否标准,我将写一个关于此的问题。