模板推导和隐式构造函数:有没有办法使模板推导与隐式转换一起工作?

问题描述 投票:0回答:2

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

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

我的想法:

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

编辑:一些背景: 在我的实际代码中,

A
是一个(大)容器,
B
是一个轻量级的非拥有视图对象。这种情况类似于在推导
std::vector<T>
std::span<T>
不能隐式转换为
T
,即使对于任何具体的
T
,这样的转换都存在。

c++ templates implicit-conversion template-argument-deduction
2个回答
1
投票

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

基本上,当编译器看到

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

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

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

要解决这个问题,您可以做的是允许编译器推导模板,然后显式构造

B<T>

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

1
投票

我认为你可以提供不同的

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

© www.soinside.com 2019 - 2024. All rights reserved.