【问题标题】:Template Deduction of Base Template Parameters through Smart Pointers and Conversion通过智能指针和转换对基础模板参数进行模板推导
【发布时间】:2018-10-21 11:42:17
【问题描述】:

完整示例请参见编译器资源管理器:https://godbolt.org/z/_rVFvO

给定一个抽象模板类Runnable 和一个继承自Runnable<int> 的实现Derived

#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;
}

另外,有没有办法通过模板类推导指南或使用不同的智能指针来支持类似的用法(在我的应用程序中拥有原始指针和非生命周期扩展引用会很困难)。

【问题讨论】:

    标签: c++ templates polymorphism


    【解决方案1】:

    代替:

    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)...);
    }
    

    (查看完整代码here

    这不是 100% 等效的,因为提议的解决方案接受 Derived 类型的 std::unique_ptr,而原始代码将仅接受基本类型。

    原始代码不起作用,因为模板会匹配需要转换的类型。在您的代码中,两个 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)...);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-22
      相关资源
      最近更新 更多