Использование decltype в конце указанного возврата в базовом классе CRTP - PullRequest
6 голосов
/ 21 февраля 2011

Я пытаюсь использовать decltype в позднем указанном возврате функции-члена в базовом классе CRTP, и происходит ошибка: invalid use of incomplete type const struct AnyOp<main()::<lambda(int)> >.

template<class Op>
struct Operation
{
    template<class Foo>
    auto operator()(const Foo &foo) const ->
        typename std::enable_if<is_foo<Foo>::value,
                                decltype(static_cast<const Op*>(nullptr)->call_with_foo(foo))>::type     
    {
        return static_cast<const Op*>(this)->call_with_foo(foo);
    }
};


template<class Functor>
struct AnyOp : Operation<AnyOp<Functor> >
{
    explicit AnyOp(Functor func) : func_(func) {}

    template<class Foo>
    bool call_with_foo(const Foo &foo) const
    {
        //do whatever
    }

  private:
    Functor func_;
};

Я в основном пытаюсь переместить всепанели sfinae в базовый класс, поэтому мне не нужно повторять ее для каждой операции, которую я создаю (в настоящее время каждая операция имеет 6 различных вызовов, и существует ~ 50 операций, поэтому повторений с enable_if достаточно много).

Я пробовал решение, которое основывалось на перегрузке, но один из типов, который может быть передан, - это все, что можно вызвать (это может быть обычный функтор из C ++ 03 или C ++ 0x лямбда)который я привязал к std :: function, к сожалению, накладные расходы от std :: function, хотя и очень минимальные, на самом деле имеют значение в этом приложении.

Есть ли способ исправить то, что у меня есть или есть?Есть ли лучшее решение для решения этой проблемы?

Спасибо.

Ответы [ 2 ]

6 голосов
/ 21 февраля 2011

Вы, как уже описывает другой ответ, пытаетесь получить доступ к члену класса в одном из базовых классов класса. Это потерпит неудачу, потому что член еще не объявлен в тот момент.

Когда он создает экземпляр базового класса, он создает все объявления своих членов, поэтому ему нужно знать тип возвращаемого значения. Вы можете сделать тип возвращаемого значения зависимым от Foo, что задерживает вычисление типа возврата до тех пор, пока не станет известен Foo. Это изменит базовый класс, как показано ниже

// ignore<T, U> == identity<T>
template<typename T, typename Ignore> 
struct ignore { typedef T type; };

template<class Op>
struct Operation
{
    template<class Foo>
    auto operator()(const Foo &foo) const ->
        typename std::enable_if<is_foo<Foo>::value,
           decltype(static_cast<typename ignore<const Op*, Foo>::type>(nullptr)->call_with_foo(foo))>::type     
    {
        return static_cast<const Op*>(this)->call_with_foo(foo);
    }
};

Это искусственно делает приведение static_cast к типу, зависящему от Foo, поэтому для него не требуется сразу полный тип Op. Скорее тип должен быть завершен, когда operator() создается с соответствующим аргументом шаблона.

1 голос
/ 21 февраля 2011

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

Другой вариант, в зависимости от того, насколько вы гибки в изменении иерархии классов (и помните, что у вас есть шаблон typedefs), состоит в том, чтобы обертка наследовала от класса реализации, а не наоборот. Например, вы можете написать AddParensWrapper<T>, который наследуется от T и имеет operator(), который переходит к T::call_with_foo. Это решит проблему зависимости.

...