我正在将大型代码转换为使用自定义共享指针而不是原始指针。我在重载解析方面遇到问题。考虑这个例子:
#include <iostream>
struct A {};
struct B : public A {};
void f(const A*)
{
std::cout << "const version\n";
}
void f(A*)
{
std::cout << "non-const version\n";
}
int main(int, char**)
{
B* b;
f(b);
}
这段代码正确地写出了“非常量版本”,因为资格转换在隐式转换序列的排序中发挥作用。现在看一下使用shared_ptr的版本:
#include <iostream>
#include<memory>
struct A {};
struct B : public A {};
void f(std::shared_ptr<const A>)
{
std::cout << "const version\n";
}
void f(std::shared_ptr<A>)
{
std::cout << "non-const version\n";
}
int main(int, char**)
{
std::shared_ptr<B> b;
f(b);
}
此代码无法编译,因为函数调用不明确。
我明白那个自定义推导指南这将是一个解决方案,但它在 Visual Studio 中仍然不存在。
我正在使用正则表达式转换代码,因为有数千个这样的调用。正则表达式无法区分与 const 版本匹配的调用和与非 const 版本匹配的调用。使用共享指针时是否可以更好地控制重载决策,并避免手动更改每个调用?当然,我可以 .get() 原始指针并在调用中使用它,但我想完全消除原始指针。
您可以引入额外的重载来为您进行委托:
template <class T>
void f(std::shared_ptr<T> a)
{
f(std::static_pointer_cast<A>(a));
}
template <class T>
void f(std::shared_ptr<const T> a)
{
f(std::static_pointer_cast<const A>(a));
}
您还可以使用std::enable_if
将第一次过载限制为非const
T
s,和/或将两个重载限制为T
s 源自A
.
这是如何运作的:
你有一个std::shared_ptr<X>
对于一些X
这两者都不是A
nor const A
(它要么是B
or const B
)。如果没有我的模板重载,编译器必须选择转换它std::shared_ptr<X>
到任一std::shared_ptr<A>
or std::shared_ptr<const A>
。从排名来看,两者都是同样好的转化(都是用户定义的转化),因此存在歧义。
添加模板重载后,有四种参数类型可供选择(我们来分析一下X = const B
case):
std::shared_ptr<A>
std::shared_ptr<const A>
-
std::shared_ptr<const B>
从第一个模板实例化,T = const B
.
-
std::shared_ptr<const B>
从第二个模板实例化,T = B
.
显然类型 3 和 4 比类型 1 和 2 更好,因为它们根本不需要转换。因此将选择其中之一。
类型 3 和 4 本身是相同的,但随着模板的重载解析,引入了附加规则。即,“更专业”(更多非模板签名匹配)的模板比不那么专业的模板更受青睐。由于过载 4 有const
在签名的非模板部分(在T
),它更专业,因此被选择。
没有规则说“模板更好”。事实上,恰恰相反:当模板和非模板的成本相同时,非模板是首选。这里的技巧是模板有lesser成本(无需转换)比非模板(需要用户定义的转换)便宜。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)