2010-04-29 3 views
13

Это вопрос к my previous question.Вложенные выражения привязки

#include <functional> 

int foo(void) {return 2;} 

class bar { 
public: 
    int operator() (void) {return 3;}; 
    int something(int a) {return a;}; 
}; 

template <class C> auto func(C&& c) -> decltype(c()) { return c(); } 

template <class C> int doit(C&& c) { return c();} 

template <class C> void func_wrapper(C&& c) { func(std::bind(doit<C>, std::forward<C>(c))); } 

int main(int argc, char* argv[]) 
{ 
    // call with a function pointer 
    func(foo); 
    func_wrapper(foo); // error 

    // call with a member function 
    bar b; 
    func(b); 
    func_wrapper(b); 

    // call with a bind expression 
    func(std::bind(&bar::something, b, 42)); 
    func_wrapper(std::bind(&bar::something, b, 42)); // error 

    // call with a lambda expression 
    func([](void)->int {return 42;}); 
    func_wrapper([](void)->int {return 42;}); 

    return 0; 
} 

Я получаю ошибки компиляции, глубоко в заголовках C++:

functional:1137: error: invalid initialization of reference of type ‘int (&)()’ from expression of type ‘int (*)()’
functional:1137: error: conversion from ‘int’ to non-scalar type ‘std::_Bind<std::_Mem_fn<int (bar::*)(int)>(bar, int)>’ requested

func_wrapper (Foo) предполагается выполнить FUNC (пустяк (Foo)). В реальном коде он упаковывает функцию для выполнения потока. func выполняет функцию, выполняемую другим потоком, doit находится между ними, чтобы проверять необработанные исключения и очищать. Но дополнительная привязка в func_wrapper messes things up ...

+0

возможно удалить тег C++?это прямой C++ 0x – Anycorn

+10

Сохраните оба тега. C++ 0x тоже C++. – jalf

+0

Дайте PC-Lint вихрь (с www.gimpel.com). Это неплохо дает гораздо лучшие и подробные сообщения об ошибках, чем визуальная студия. Однако это довольно дорого. –

ответ

2

Рассматривая это во второй раз сейчас, и я думаю, что у меня есть правдоподобное объяснение первой ошибки, которую вы видите.

В этом случае более полезно посмотреть на полную ошибку и экземпляры шаблонов, которые приводят к этому. Ошибка печататься мой компилятор (GCC 4.4), например, заканчивается следующими строками:

test.cpp:12: instantiated from ‘decltype (c()) func(C&&) [with C = std::_Bind<int (*(int (*)()))(int (&)())>]’ 
test.cpp:16: instantiated from ‘void func_wrapper(C&&) [with C = int (&)()]’ 
test.cpp:22: instantiated from here 
/usr/include/c++/4.4/tr1_impl/functional:1137: error: invalid initialization of reference of type ‘int (&)()’ from expression of type ‘int (*)()’ 

Теперь глядя на это снизу вверх, фактическое сообщение об ошибке кажется правильным; типы, которые компилятор вывел , являются несовместимыми.

Первый экземпляр шаблона, на func_wrapper, ясно показывает, какой тип компилятор вывел из фактического параметра foo в func_wrapper(foo). Я лично ожидал, что это будет указателем функции, но на самом деле это функция ссылка.

Второй экземпляр шаблона вряд ли читается. Но возиться с std::bind немного, я узнал, что формат текстового представления ССАГПЗ оттисков для связывания функтора примерно:

std::_Bind<RETURN-TYPE (*(BOUND-VALUE-TYPES))(TARGET-PARAMETER-TYPES)> 

Так разрывают его на части:

std::_Bind<int (*(int (*)()))(int (&)())> 
// Return type: int 
// Bound value types: int (*)() 
// Target parameter types: int (&)() 

Это где несовместимый типы начинаются. По-видимому, хотя c в func_wrapper является ссылкой на функцию, он превращается в функцию указатель, однажды переданный в std::bind, что приводит к несовместимости типа. В этом случае std::forward вообще не имеет значения.

Мое рассуждение здесь состоит в том, что std::bind только, кажется, заботится о значениях, а не о ссылках. В C/C++ нет такой вещи, как значение функции; есть только ссылки и указатели. Поэтому, когда ссылка на функцию разыменовывается, компилятор может только осмысленно дать вам указатель на функцию.

Единственный контроль над этим - это параметры вашего шаблона. Вам нужно будет сообщить компилятору, что вы имеете дело с указателем функции с самого начала, чтобы сделать эту работу. Это, вероятно, то, что вы имели в виду.Чтобы сделать это, явно указать тип, который вы хотите для параметра шаблона C:

func_wrapper<int (*)()>(foo); 

или более короткого решения, явно взять адрес функции:

func_wrapper(&foo); // with C = int (*)() 

Я вернусь к вам, если Я когда-либо выяснял вторую ошибку. :)

1

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

  • : При использовании вложенного зЬй :: связывайте внутренний станд :: привязывать оценивается первым, и возвращение значение будет заменено на своем месте, пока будет оценен внешний std :: bind. Это означает, что std::bind(f, std::bind(g, _1))(x) выполняет то же самое, что и f(g(x)). Внутренний std :: bind предполагается обернуть std :: ref, если внешний std :: bind требует функтора, а не возвращаемого значения.

  • b: Ссылка на значение r не может быть правильно перенаправлена ​​на функцию с помощью std :: bind. И reason уже подробно проиллюстрирован.

Итак, давайте рассмотрим вопрос. Наиболее важное значение функции здесь может быть func_wrapper, который предназначен для выполнения 3 цели:

  1. Совершенных экспедиторской функтор в Doit шаблона функции в первом,
  2. затем с помощью зОго :: привязки сделать пустяк как замыкание,
  3. и запуск шаблона функции func выполняет функцию, возвращаемую методом std :: bind.

В соответствии с пунктом b цель 1 не может быть выполнена. Итак, давайте забудем, что идеальная функция переадресации и doit должна принять параметр ссылки l-value.

Согласно пункту а цель 2 будет выполняться с использованием std :: ref.

В результате, окончательный вариант может быть:

#include <functional> 

int foo(void) {return 2;} 

class bar { 
public: 
    int operator() (void) {return 3;}; 
    int something(int a) {return a;}; 
}; 

template <class C> auto func(C&& c) -> decltype(c()) { return c(); } 

template <class C> int doit(C&/*&*/ c) // r-value reference can't be forwarded via std::bind 
{ 
    return c(); 
} 

template <class C> void func_wrapper(C&& c) 
{ 
    func(std::bind(doit<C>, 
        /* std::forward<C>(c) */ // forget pefect forwarding while using std::bind 
        std::ref(c)) // try to pass the functor itsself instead of its return value 
     ); 
} 

int main(int argc, char* argv[]) 
{ 
    // call with a function pointer 
    func(foo); 
    func_wrapper(foo); // error disappears 

    // call with a member function 
    bar b; 
    func(b); 
    func_wrapper(b); 

    // call with a bind expression 
    func(std::bind(&bar::something, b, 42)); 
    func_wrapper(std::bind(&bar::something, b, 42)); // error disappears 

    // call with a lambda expression 
    func([](void)->int {return 42;}); 
    func_wrapper([](void)->int {return 42;}); 

    return 0; 
} 

Но, если вы действительно хотите достичь цели 1 и 2, как? Попробуйте это:

Позвольте мне объяснить некоторые моменты:

  • Чтобы уменьшить много обратных заявлений, изменение функтора подпись междунар() к мочеиспусканию().
  • Шаблоны функций 2 run() используются для проверки того, переадресован ли исходный параметр функтора или нет.
  • dispatcher_traits собирается на карте bool constant набирать.
  • Лучше назовите диспетчера: вперед, чтобы отличаться от диспетчера :: отправка или вы должны вызвать шаблон std :: bind с диспетчером :: подпись переднего плана.
Смежные вопросы