【问题标题】:template deduction and implicit constructors模板推导和隐式构造函数
【发布时间】:2023-03-09 14:03:02
【问题描述】:

有没有办法使模板推导与(隐式)转换一起工作?比如下面的例子:

template<typename T> struct A {};

template<typename T> struct B
{
    B(A<T>); // implicit A->B conversion
};

template<typename... Ts> void fun(B<Ts>...);

int main()
{
    A<int> a;
    fun(B(a)); // works
    fun(a);    // does not work (deduction failure)
}

我的想法:

  • 如果AB 的子类,则一切正常。这意味着演绎可以使用向上转换进行隐式转换。所以它不能使用构造函数进行隐式转换似乎很奇怪。
  • 原则上可以为AB重载fun,但对于多个参数,组合太多了
  • 添加扣除指南 (template&lt;typename T&gt; B(A&lt;T&gt;)-&gt;B&lt;T&gt;;) 不会改变任何内容。

编辑:一些上下文: 在我的实际代码中,A 是一个(大)容器,B 是一个轻量级的非拥有视图对象。这种情况类似于std::vector&lt;T&gt;在推演T时不能隐式转换为std::span&lt;T&gt;,即使对于任何具体的T,都存在这样的转换。

【问题讨论】:

    标签: c++ templates implicit-conversion template-argument-deduction


    【解决方案1】:

    模板参数推导不考虑任何潜在的类型转换 - 主要是因为推导发生在任何此类转换发生之前。

    基本上,当编译器看到fun(a) 时,它首先会收集一组有资格为该调用提供服务的foo 函数。如果找到foo 函数模板,编译器会尝试通过将模板参数替换为调用语句中传递的参数类型来从中生成具体函数。这就是模板参数推导发生的地方。这里不能发生类型转换,因为没有要转换的具体类型。在您的示例中,B&lt;T&gt; 不是要转换为的类型,因为 T 是未知的,并且无法从 A&lt;int&gt; 类型的参数中发现它。也不知道B&lt;T&gt; 的任意实例是否可以从A&lt;int&gt; 构造,因为B 的不同特化可能有不同的构造函数集。因此,在您的情况下,扣除失败。

    如果推导成功,则将具体函数(带有推导的参数类型)添加到候选集中。

    当收集到候选集时,将选择最匹配的候选集。此时考虑参数转换。转换是可能的,因为候选不再是模板,并且转换的目标类型是已知的。

    您可以做的解决方法是让编译器也推断出模板,然后显式构造B&lt;T&gt;

    template<typename... Ts> void fun_impl(B<Ts>...);
    
    template<template<typename> class X, typename... Ts>
    std::enable_if_t<(std::is_constructible_v<B<Ts>, X<Ts>> && ...)> fun(X<Ts>... args)
    {
        fun_impl(B<Ts>(args)...);
    }
    
    int main()
    {
        A<int> a;
        fun(B(a)); // works, instantiates fun<B, int>
        fun(a);    // works, instantiates fun<A, int>
    }
    

    【讨论】:

    • 感谢您的解释。所有问题都可以通过额外的间接层来解决,看起来 xD 。尽管我将进一步修改解决方案以支持将 A 和 B 的组合传递给 fun。还需要通过引用传递 A (我最初没有提到这一点。在问题中添加了一些上下文来说明这一点)。但我可能自己也能弄清楚。再次感谢:)
    【解决方案2】:

    我认为您可以提供不同的 func 重载,然后您想出并解决您的多个可能转换的问题。

    template<typename T> struct A {};
    template<typename T> struct C {};
    
    template<typename T> struct B
    {
        B(A<T>) {}
        B(C<T>) {}
    };
    
    template<typename... Ts>
    void fun(B<Ts>...)
    {
        LOGFUN;
    }
    
    template<typename... Ts>
    void fun(Ts...arg)
    {
        LOGFUN;
        fun(B{arg}...);
    }
    
    int main()
    {
        A<int> a;
        C<int> c;
        fun(B(a));
        fun(a);
        fun(c, a, c);
    }
    

    https://godbolt.org/z/ne31z8Kfa

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-12
      • 1970-01-01
      相关资源
      最近更新 更多