Функция C++ связывает повторяющиеся аргументы с функцией curried

Я пытаюсь понять концепцию карринга и вызова функции, которая объединяет три строки, Но передает только две строки и дважды использует второй аргумент.

однако, когда я это делаю, второй аргумент вообще не отправляется функции, и он печатает пустую строку. Это какая-то очевидная ошибка?

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 дважды означает, что второй аргумент будет передан как для второго, так и для второго третий. Если a использует строку на своем месте, она работает нормально.

enter image description here

2 ответов


копирование строк дорого. С std::bind думает, что значения заполнителей используются только один раз, он выполняет std::move На струнах. Это делается для каждого параметра и, как следствие, либо b или c - это переезжает, что означает пустую строку.

вы можете изменить это поведение, явно говоря, что вы имеете в виду, передавая аргументы по const-reference:

string concatthreestrings(string const& a,string const& b,string const& c)

теперь он должен работать.


я сделал несколько тестов, используя этот меньший пример, который показывает то же поведение, что и у вас:

#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 ключевое слово, так что типа 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 нет. Я все еще не знаю, является ли это стандартным, я собираюсь написать вопрос об этом.