Функция C ++ связывает повторяющиеся аргументы с функцией карри - PullRequest
7 голосов
/ 30 марта 2012

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

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

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 ]

5 голосов
/ 30 марта 2012

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

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

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

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

2 голосов
/ 04 апреля 2012

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

#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 и прекрасно их пересылает. Вместо использования собственного typedef, вы должны использовать ключевое слово auto, чтобы компилятор автоматически определял тип func. Если мы изменим основную часть следующим образом, мы получим ожидаемое поведение:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...