Ожидаемый тип, получил шаблон - PullRequest
0 голосов
/ 02 мая 2018

Я пишу что-то вроде "асинхронной фабрики", где трудоемкие конструкции конкретных объектов откладываются до std::async задач. Каждый AsyncFactory будет хранить умный указатель на объект.

[Это не самое правильное применение Factory Pattern , но это ради MWE].

#include <future>
#include <memory>
#include <type_traits>
#include <cassert>

/**
 * @param I is the interface type
 * @param Ptr is the memory handler. Default = unique; optional = shared
 */
template <class I, template<class> class Ptr = std::unique_ptr>
class AsyncFactory
{
    /**
     * @param C - the concrete type for the interface I
     * @param Ts - the variadic params
     */
    template <class C, typename... Ts>
    void _future_reload(Ts&&... params)
    {
        if (std::is_same<Ptr, std::unique_ptr>())                       // line21
        {
            ptr = std::make_unique<C>(std::forward<Ts>(params)...);
        }
        else
        {
            if (std::is_same<Ptr, std::shared_ptr>())                   // line27
            {
                ptr = std::make_shared<C>(std::forward<Ts>(params)...);
            }
            else
            {
                static_assert(0, "unacceptable type for smart pointer");// line33
            }
        }
    }

public: 

    Ptr<I> ptr;

    AsyncFactory() :
        ptr(nullptr)
    {}

    /**
     * @param C - the concrete type. Default: the interface type
     * @param Ts - the variadic params
     */
    template <class C = I, typename... Ts>
    void reload(Ts&&... params)
    {
        std::future<void> fReload =
                        std::async(std::launch::async,
                        &AsyncFactory::_future_reload<C, Ts...>, this,
                        std::forward<Ts>(params)...);
    }
};


class BaseVirtual
{
    virtual void foo() = 0;
};

class DerivedConcrete :
    public BaseVirtual
{
    void foo() override {;}
};


int main()
{
    AsyncFactory<BaseVirtual, std::shared_ptr> fac;

    fac.reload<DerivedConcrete>();
}

Проблемы возникают с умным указателем. Я должен вызывать разные make rs для unique / shared указателей. Но g++ -std=c++14 останавливается с

f.cpp: In member function ‘void AsyncFactory<I, Ptr>::_future_reload(Ts&& ...)’:
f.cpp:21:44: error: type/value mismatch at argument 1 in template parameter list for ‘template<class, class> struct std::is_same’
   if (std::is_same<Ptr, std::unique_ptr>())
                                        ^
f.cpp:21:44: note:   expected a type, got ‘Ptr’
f.cpp:21:44: error: type/value mismatch at argument 2 in template parameter list for ‘template<class, class> struct std::is_same’
f.cpp:21:44: note:   expected a type, got ‘unique_ptr’
f.cpp:27:45: error: type/value mismatch at argument 1 in template parameter list for ‘template<class, class> struct std::is_same’
    if (std::is_same<Ptr, std::shared_ptr>())
                                         ^
f.cpp:27:45: note:   expected a type, got ‘Ptr’
f.cpp:27:45: error: type/value mismatch at argument 2 in template parameter list for ‘template<class, class> struct std::is_same’
f.cpp:27:45: note:   expected a type, got ‘shared_ptr’
f.cpp:33:9: error: static assertion failed: unacceptable type for smart pointer
     static_assert(0, "unacceptable type for smart pointer");

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Вы не можете сравнивать параметры шаблона шаблона таким образом, вы можете сравнивать только конкретные типы с std::is_same. Более того, вы не можете даже иметь одну и ту же подпись шаблона AsyncFactory для std::shared_ptr и std::unique_ptr: https://godbolt.org/g/tNiYB1

Решением вашей проблемы является введение еще одного уровня абстракции:

struct UseUnique
{
    template<class I>
    using Ptr = std::unique_ptr<I>;

    template<class C, typename... Ts>
    static auto build(Ts&&... params)
    {
        return std::make_unique<C>(std::forward<Ts>(params)...);
    }
};

struct UseShared
{
    template<class I>
    using Ptr = std::shared_ptr<I>;

    template<class C, typename... Ts>
    static auto build(Ts&&... params)
    {
        return std::make_shared<C>(std::forward<Ts>(params)...);
    }
};

Эти структуры содержат информацию, необходимую для определения вашего члена и построения конкретных типов (т. Е. Какой тип указателя и какую make_X функцию использовать). Тогда вы можете сделать:

template <class I, class UseWhat = UseShared>
class AsyncFactory
{
    template <class C, typename... Ts>
    void _future_reload(Ts&&... params)
    {
        ptr = UseWhat::template build<C>(std::forward<Ts>(params)...);
    }

public:
    using Ptr = typename UseWhat::template Ptr<I>;
    Ptr ptr;
    // ...
}

и, следовательно,

AsyncFactory<Interf, UseUnique> fac;
fac.reload<Concrete>();

Полный рабочий код здесь: https://godbolt.org/g/L2511J

Структуры, возможно, должны находиться в отдельном пространстве имен и, возможно, иметь более подходящие имена. Оставлено в качестве упражнения для читателя:)

0 голосов
/ 02 мая 2018

std::unique_ptr не тип. std::unique_ptr<int> это тип. Вам нужно передать явные параметры шаблона, чтобы использовать его внутри is_same.

Кроме того, вы, вероятно, не хотите использовать if таким образом, поскольку обе ветви должны быть действительными независимо от результата is_same. В C ++ 17 вы бы использовали if constexpr(...) для решения этой проблемы - в C ++ 14 вы можете использовать более традиционный подход, основанный на перегрузке, или подход, основанный на диспетчеризации тегов. Э.Г.

auto impl(std::true_type  /* is shared ptr */) { /* ... */ }
auto impl(std::false_type /* is unique ptr */) { /* ... */ }

Использование:

ptr = impl(std::is_same<Ptr, std::shared_ptr<T>>{}, /* ... */);
...