代码之家  ›  专栏  ›  技术社区  ›  Araeos

通过智能指针和转换对基本模板参数进行模板推导

  •  1
  • Araeos  · 技术社区  · 7 年前

    有关完整示例,请参阅编译器资源管理器: 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 回复  |  直到 7 年前
        1
  •  2
  •   Michael Veksler    7 年前

    而不是:

    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%等效的,因为建议的解决方案接受 std::unique_ptr 而原始代码只接受基类型。

    原始代码不起作用,因为模板与需要转换的类型匹配。在你的代码中,两个 STD:UngQuyPPTR 类型不是彼此的基/派生类型,因此模板将不匹配。

    对于建议的解决方案,函数接受原始的 STD:UngQuyPPTR ,前提是它可以转换为函数内部的基类型。这个 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)...);
    }