2012-03-30 1 views
7

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

enter image description here

+0

Очень интересный вопрос, я бы не ожидал такого поведения! –

+1

Просто добавьте исправление: на самом деле это не карри. Привязанными аргументами и каррированием являются две очень похожие, но все же разные операции, которые не следует путать. Currying означает функцию, которая принимает функцию N аргументов и превращает ее в функцию одного аргумента, которая возвращает функцию одного аргумента, которая возвращает функцию одного аргумента ... (repeat n Times). Вы можете использовать 'std :: bind' для реализации функции curry, которая делает это для вас (в некоторой степени). Аналогичным образом вы можете использовать currying для реализации привязки аргументов в способе 'std :: bind'. – LiKao

+1

@LiKao: Действительно, 'bind' разрешает [частичное приложение] (http://en.wikipedia.org/wiki/Partial_application), а не currying. –

ответ

5

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

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

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

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

+2

Вы уверены, что это поведение определено? Из того, что я прочитал в стандарте, обертка, возвращаемая 'bind', пересылает свои аргументы в завернутую функцию. В этом случае, по моему мнению, у нас была бы обертка по этому пути (после вывода параметров шаблона и сбрасывания): 'string g (const char (& u1) [6], const char (& u2) [7]) { concatthreestrings (forward (u1), forward (u2), forward (u2)); } '. Три строки 'a',' b' и 'c' будут построены из массивов, поэтому я не вижу, где будет происходить движение. –

+0

@ipc: Есть ли способ указать это поведение в std :: bind, а не изменять само определение функции – ganeshran

+0

Доступ к перемещенному объекту - UB. –

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по значению, в то время как обертка, возвращаемая связыванием, вероятно, принимает свои аргументы с помощью 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 

Другим решением является заменить ЬурейуЮ так, что func берет свой параметр в виде ссылки-на-ПОСТОЯННЫЙ:

typedef std::function< void(string const &) > fn_t; 

Я не «По-настоящему понять, почему другой typedef не работает ... Предположительно, строка перемещается, как заметил @ipc, но я не знаю, в какой момент выполнения это происходит. Я даже не уверен, что это стандартное поведение, поскольку как function, так и обертка, возвращаемая bind, должны использовать идеальную пересылку. Возможно, GCC включает некоторые оптимизации, которые перемещают аргументы оболочки, когда они передаются по значению?

Редактировать

Я сделал несколько тестов, оказывается реализация GCC по std::function выполняет движение по своим аргументам, в то время как возвращение обертки std::bind не делает. Я до сих пор не знаю, стандартно ли это, я собираюсь написать вопрос об этом.

+0

ya Я думаю, что это проблема с реализацией компилятора, поскольку VC его не показывает. – ganeshran

Смежные вопросы