Вычисление шаблона из базовых параметров шаблона с помощью интеллектуальных указателей и преобразования - PullRequest
0 голосов
/ 21 октября 2018

Полный пример см. В Проводнике компилятора: https://godbolt.org/z/_rVFvO

С учетом абстрактного класса шаблона Runnable и реализации Derived, наследуемой от Runnable<int>:

#include <iostream>
#include <memory>

using namespace std;

template<class... Args>
struct Runnable
{
    virtual ~Runnable() = default;
    virtual void f(Args... args) const = 0;
};

struct Derived : public Runnable<int>
{
    void f(int x) const override
    {
        cout << "f(" << x << ")" << endl;
    }
};

Чтоявляется основной причиной, по которой вывод параметра шаблона для функции accept_variadic завершается неудачно, если смарт-указатель на производный тип?

template<class... Args>
void accept_variadic(std::unique_ptr<Runnable<Args...>> o, Args&&... args)
{
    o->f(forward<Args>(args)...);
}
int main()
{
    accept_variadic(make_unique<Derived>(), 5); // Error (no conversion)
    return 0;
}

Но если ссылка (или указатель) напрямую без смарт-указателя работает:

template<class... Args>
void accept_variadic_ref(const Runnable<Args...>& o, Args&&... args)
{
    o.f(forward<Args>(args)...);
}
int main()
{
    accept_variadic_ref(Derived(), 5); // OK
    return 0;
}

Кроме того, есть ли способ поддержать подобное использование с помощью руководств по выводам из класса шаблонов или с использованием другого интеллектуального указателя (в моем приложении было бы сложно иметь необработанные указатели и ссылки, не увеличивающие время жизни).

1 Ответ

0 голосов
/ 21 октября 2018

Вместо:

template<class... Args>
void accept_variadic(std::unique_ptr<Runnable<Args...>> o, Args&&... args)
{
    o->f(forward<Args>(args)...);
}

Вы можете использовать TMP, чтобы получить то, что вы хотите:

template<class T, class... Args>
std::enable_if_t<std::is_convertible_v<std::unique_ptr<T>, 
                 std::unique_ptr<Runnable<Args...>>>>
accept_variadic(std::unique_ptr<T> o, Args&&... args)
{
    o->f(forward<Args>(args)...);
}

(полный код здесь )

Это не на 100% эквивалентно, поскольку предлагаемое решение принимает std::unique_ptr типа Derived, в то время как исходный код будет принимать только базовый тип.

Исходный код не работает, поскольку шаблоны соответствуют типам, которые требуют преобразования.В вашем коде два типа std::unique_ptr не являются базовыми / производными друг от друга, поэтому шаблоны не будут совпадать.

В предлагаемом решении функция принимает исходный std::unique_ptr при условии, что его можно преобразовать в базовый тип внутри функции.std::enable_if_t гарантирует, что другие типы не совпадают, только те, которые могут быть преобразованы в базу.

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

В некоторых вариантах вопроса первоначальное решение может бытьпроблематичным.Это может произойти в вариантах вопросов, в которых вызов f() в базовом классе обрабатывается иначе, чем вызов f() в производном классе.Есть несколько возможностей, когда это может произойти (но не в оригинальном вопросе).Чтобы преодолеть этот риск, accept_variadic() следует изменить на:

template<class T, class... Args>
std::enable_if_t<std::is_convertible_v<T&, Runnable<Args...>&>>
accept_variadic(std::unique_ptr<T> o, Args&&... args)
{
    // could also be solved with 
    // std::unique_ptr<Runnable<Args...>> base = std::move(o);
    Runnable<Args...> & runnable = *o;
    runnable.f(forward<Args>(args)...);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...