C ++函数将重复参数绑定到curried函数

时间:2012-03-30 14:27:10

标签: c++ gcc currying

我试图理解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两次意味着第二个参数将被传递给第二个和第三个。如果在其位置使用字符串,则其工作正常。

enter image description here

2 个答案:

答案 0 :(得分:5)

复制字符串很昂贵。由于std::bind认为占位符的值仅使用一次,因此它会对字符串执行std::move。这是针对每个参数完成的,因此,bc被移动,这意味着空字符串。

您可以通过使用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所说,但我不知道执行的这一点发生在什么时候。我甚至不确定这是标准行为,因为functionbind返回的包装器都应该使用完美转发。也许GCC包含一些优化,当它们通过值传递包装器参数时会移动它?

修改

我做了一些测试,结果发现GCC的std::function实现对其参数执行了一次移动,而std::bind的包装返回却没有。我仍然不知道这是否标准,我将写一个关于此的问题。