从继承类型推导模板参数
Template argument deduction from inherited type
我想要如下设置:
template <typename T> class a {};
class b : public a<int> {};
template <typename T>
void do_foo(std::unique_ptr<a<T>> foo)
{
// Do something with foo
}
int main()
{
do_foo(std::make_unique<b>());
}
编译失败,并附有 template argument deduction/substitution failed
和 mismatched types 'a<T>' and 'b'
注释。这是不言自明的。我可以通过编写 do_foo<int>(std::make_unique<b>());
来帮助编译器,但是我通过编写两次 int
来重复自己。
有没有办法让编译器在这种情况下推导出模板参数?你会怎么称呼这种行为?我尝试搜索“继承类型的模板类型推导”、“多态模板推导”等内容
Is there a way to get the compiler to deduce the template parameter in this case?
没有。不在 C++14(甚至 C++20)中。
And what would you call this behaviour?
符合标准。具体来说,这段适用于:
[temp.deduct.call]
4 In general, the deduction process attempts to find template
argument values that will make the deduced A
identical to A
(after
the type A
is transformed as described above). However, there are
three cases that allow a difference:
- If the original
P
is a reference type, the deduced A
(i.e., the type referred to by the reference) can be more cv-qualified than the
transformed A
.
- The transformed
A
can be another pointer or pointer to member type that can be converted to the deduced A
via a qualification
conversion ([conv.qual]).
- If
P
is a class and P
has the form simple-template-id, then the transformed A
can be a derived class of the deduced A
.
Likewise, if P
is a pointer to a class of the form
simple-template-id, the transformed A
can be a pointer to a derived class pointed to by the deduced A
.
这是一个详尽的案例列表,其中可以从函数参数有效地推导出模板参数,即使它与函数参数的模式不匹配完全。第一个和第二个项目符号处理诸如
之类的事情
template<class A1> void func(A1&){}
template<class A2> void func(A2*){}
int main() {
const int i = 1;
func(i); // A1 = const int
func(&i); // A2 = const int
}
第三个项目符号与我们的情况最接近。派生自模板特化的 class 可用于推导出属于其基础的模板参数。为什么它在您的情况下不起作用?因为函数模板参数是 unique_ptr<a<T>>
而你调用它的参数是 unique_ptr<b>
。 unique_ptr
专业化 本身 与继承无关。所以他们不匹配子弹,推演失败。
但这并不意味着像 unique_ptr
这样的包装器可以完全阻止模板参数推导。例如:
template <typename> struct A {};
struct B : A<int> {};
template<typename> struct wrapper{};
template<> struct wrapper<B> : wrapper<A<int>> {};
template<typename T>
void do_smth(wrapper<A<T>>) {}
int main() {
do_smth(wrapper<B>{});
}
在这种情况下,wrapper<B>
派生自 wrapper<A<int>>
。所以第三点是适用的。并且通过模板参数推导的复杂(递归)过程,它允许 B
匹配 A<T>
并推导 T = int
.
TL;DR: unique_ptr<T>
特化无法复制原始指针的行为。它们不继承 unique_ptr
的特化而不是 T
的基础。也许如果反射出现在 C++ 中,我们将能够对 确实 以这种方式运行的智能指针进行元编程。
作为解决方法,您可以添加重载:
template <typename T>
void do_foo_impl(a<T>* foo)
{
return do_foo(std::unique_ptr<a<T>>(foo));
}
template <typename T>
auto do_foo(std::unique_ptr<T> foo) -> decltype(do_foo_impl(foo.release()))
{
do_foo_impl(foo.release());
}
我想要如下设置:
template <typename T> class a {};
class b : public a<int> {};
template <typename T>
void do_foo(std::unique_ptr<a<T>> foo)
{
// Do something with foo
}
int main()
{
do_foo(std::make_unique<b>());
}
编译失败,并附有 template argument deduction/substitution failed
和 mismatched types 'a<T>' and 'b'
注释。这是不言自明的。我可以通过编写 do_foo<int>(std::make_unique<b>());
来帮助编译器,但是我通过编写两次 int
来重复自己。
有没有办法让编译器在这种情况下推导出模板参数?你会怎么称呼这种行为?我尝试搜索“继承类型的模板类型推导”、“多态模板推导”等内容
Is there a way to get the compiler to deduce the template parameter in this case?
没有。不在 C++14(甚至 C++20)中。
And what would you call this behaviour?
符合标准。具体来说,这段适用于:
[temp.deduct.call]
4 In general, the deduction process attempts to find template argument values that will make the deduced
A
identical toA
(after the typeA
is transformed as described above). However, there are three cases that allow a difference:
- If the original
P
is a reference type, the deducedA
(i.e., the type referred to by the reference) can be more cv-qualified than the transformedA
.- The transformed
A
can be another pointer or pointer to member type that can be converted to the deducedA
via a qualification conversion ([conv.qual]).- If
P
is a class andP
has the form simple-template-id, then the transformedA
can be a derived class of the deducedA
. Likewise, ifP
is a pointer to a class of the form simple-template-id, the transformedA
can be a pointer to a derived class pointed to by the deducedA
.
这是一个详尽的案例列表,其中可以从函数参数有效地推导出模板参数,即使它与函数参数的模式不匹配完全。第一个和第二个项目符号处理诸如
之类的事情template<class A1> void func(A1&){}
template<class A2> void func(A2*){}
int main() {
const int i = 1;
func(i); // A1 = const int
func(&i); // A2 = const int
}
第三个项目符号与我们的情况最接近。派生自模板特化的 class 可用于推导出属于其基础的模板参数。为什么它在您的情况下不起作用?因为函数模板参数是 unique_ptr<a<T>>
而你调用它的参数是 unique_ptr<b>
。 unique_ptr
专业化 本身 与继承无关。所以他们不匹配子弹,推演失败。
但这并不意味着像 unique_ptr
这样的包装器可以完全阻止模板参数推导。例如:
template <typename> struct A {};
struct B : A<int> {};
template<typename> struct wrapper{};
template<> struct wrapper<B> : wrapper<A<int>> {};
template<typename T>
void do_smth(wrapper<A<T>>) {}
int main() {
do_smth(wrapper<B>{});
}
在这种情况下,wrapper<B>
派生自 wrapper<A<int>>
。所以第三点是适用的。并且通过模板参数推导的复杂(递归)过程,它允许 B
匹配 A<T>
并推导 T = int
.
TL;DR: unique_ptr<T>
特化无法复制原始指针的行为。它们不继承 unique_ptr
的特化而不是 T
的基础。也许如果反射出现在 C++ 中,我们将能够对 确实 以这种方式运行的智能指针进行元编程。
作为解决方法,您可以添加重载:
template <typename T>
void do_foo_impl(a<T>* foo)
{
return do_foo(std::unique_ptr<a<T>>(foo));
}
template <typename T>
auto do_foo(std::unique_ptr<T> foo) -> decltype(do_foo_impl(foo.release()))
{
do_foo_impl(foo.release());
}