Что не так с этим примером шаблонов variadic? - PullRequest
4 голосов
/ 14 апреля 2011

Базовый класс:

#include <memory>

namespace cb{

template< typename R, typename ... Args >
class CallbackBase
{
public:
    typedef std::shared_ptr< CallbackBase< R, Args... > >
            CallbackPtr;

    virtual ~CallbackBase()
    {
    }
    virtual R Call(  Args ... args) = 0;
};
} // namespace cb

Производный класс это:

namespace cb{
template< typename R, typename ... Args >
class FunctionCallback : public CallbackBase< R, Args... >
{
public:
    typedef R (*funccb)(Args...);

    FunctionCallback( funccb cb_ ) : 
        CallbackBase< R, Args... >(),
        cb( cb_ )
    {
    }
    virtual ~FunctionCallback()
    {
    }
    virtual R Call(Args... args)
    {
      return cb( args... );
    }
private:
  funccb cb;
};
} // namespace cb

Функция для создания:

namespace cb{
template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackBasePtr
    MakeCallback( typename FunctionCallback< R, Args... >::funccb cb )
{
    typename CallbackBase< R, Args... >::CallbackBasePtr
        p( new FunctionCallback< R, Args... >( cb )
);
    return p;
}
} // namespace cb

И пример:

bool Foo_1args( const int & t)
{
    return true;
}
int main()
{
    auto cbObj = cb::MakeCallback( & Foo_1args );
}

Я получаю эту ошибку:

error: no matching function for call to ‘MakeCallback(bool (*)(const int&))’
error: unable to deduce ‘auto’ from ‘<expression error>’

Я пытался изменить это, но я не мог понять, как исправить.

Итак, что не так? И как исправить этот пример?

Ответы [ 4 ]

7 голосов
/ 14 апреля 2011

Проблема может иметь смысл с более простым примером.Попробуйте определить проблему здесь:

template <typename T>
struct id { typedef T type; };

template <typename T>
void foo(typename id<T>::type x);

foo(5); // error

Проблема в том, что компилятор не может определить, каким должен быть T;он напрямую нигде не используется.Вы должны явно указать это: foo<int>(5), или позволить ему сделать вывод другим способом:

template <typename T>
void foo(typename id<T>::type x, T y);

foo(5, 7); // okay, T is int because 7 is int

Это имеет смысл: как компилятор может определить, какие T поставленыid, результат в id<T>::type совпадении?Могут быть специализации, и все это будет в любом случае дорогостоящим, если это возможно.


Аналогично, нет ничего, что компилятор мог бы вывести R и Args.Вместо этого вы должны сделать следующее:

template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackBasePtr
    MakeCallback( R cb(Args...) )
{
    typename CallbackBase< R, Args... >::CallbackBasePtr
        p( new FunctionCallback< R, Args... >( cb ));

    return p;
}

Наконец, у вас есть другие незначительные проблемы, которые необходимо исправить, , которые Xeo обрисовал в общих чертах .

4 голосов
/ 14 апреля 2011

Лучше использовать <functional>, чем изобретать его ... Кроме того, лучше копировать непосредственно из реализации вашего компилятора.

Как правило, использование меньшего количества параметров шаблона также полезно.

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

Код просто Call aФунктор не будет специализирован для различных типов функторов, поэтому он должен быть в общем случае шаблона.

Для незначительных корректировок общего шаблона лучше всего подходит класс черт.

template< typename F, typename = void >
struct callback_traits {
    typedef F const &local_type; // fallback case: only keep a reference
};

template< typename F >
struct callback_traits< F,
    typename std::enable_if< // GCC 4.4 missing is_copy_constructible:
        std::is_constructible< F, F const& >::value
    >::type > {
    typedef F local_type; // better to keep a copy as this is a callback
};

template< typename F >
struct Callback {
    typedef typename callback_traits< F >::local_type local_type;
    local_type fn;

    Callback( local_type const &fn_in ) : fn( fn_in ) {}
    template< typename ... Args >
    typename std::result_of< local_type( Args ... ) >::type
    Call( Args ... a )
        { return fn( a ... ); }
};
4 голосов
/ 14 апреля 2011

Вспомните, что я упоминал в комментариях к другим ответам:

  • Во-первых, как говорит @GMan, ваш аргумент MakeCallback был не выводимым.
  • Во-вторых, ваш тип возврата MakeCallback был неверным. Это должно быть CallbackPtr, поскольку CallbackBasePtr typedef не существует. Это привело к SFINAE срабатыванию и не рассмотрению вашей функции как возможной функции для вызова, даже когда аргумент был зафиксирован.
  • В-третьих, ваш конструктор FunctionCallback хотел указатель funccb* , в то время как funccb уже является указателем (функции), поэтому вам нужно будет передать указатель на указатель на функцию , например new FunctionCallback(&cb)
4 голосов
/ 14 апреля 2011

Исправлены некоторые type-o и специализированный MakeCallback для принятия указателей на функции. Как сказал GMan, ваши аргументы шаблона для MakeCallback находятся в не выводимом контексте.

#include <memory>

template< typename R, typename ... Args >
class CallbackBase
{
public:
    typedef std::shared_ptr< CallbackBase< R, Args... > >
            CallbackPtr;

    virtual ~CallbackBase()
    {
    }
    virtual R Call(  Args ... args) = 0;
};

template< typename R, typename ... Args >
class FunctionCallback : public CallbackBase< R, Args... >
{
public:
    typedef R (*funccb)(Args...);

    FunctionCallback( funccb  cb_ ) : 
        CallbackBase< R, Args... >(),
        cb( cb_ )
    {
    }
    virtual ~FunctionCallback()
    {
    }
    virtual R Call(Args... args)
    {
      return cb( args... );
    }
private:
  funccb cb;
};

template < typename R, typename ...Args >
typename CallbackBase< R, Args... >::CallbackPtr
    MakeCallback( R (*cb)(Args...)  )
{
    typename CallbackBase< R, Args... >::CallbackPtr
        p( new FunctionCallback< R, Args... >( cb )
);
    return p;
}

bool Foo_1args( const int & t)
{
    return true;
}
int main()
{
    auto cbObj = MakeCallback( & Foo_1args );
}

Обновление:

Стандарт C ++ определяет невыгружаемый контекст в 14.8.2.5 [temp.deduct.type], параграфы 5 - 6. Там есть маркированный список, который я не буду претендовать на полное понимание. Маркер для меня:

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

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