我经常发现我希望阻止特定构造函数或函数的缩小或签名转换(通常是Usual arithmetic conversions)。我倾向于写:
#include <iostream>
void foo(double f){
std::cout << "foo double" << f <<std::endl;
}
void foo(float) = delete;
// or template<typename T> void foo(T&& f) = delete;
void bar(unsigned int f){
std::cout << "bar uint " << f <<std::endl;
}
void bar(signed int ) = delete;
// or template<typename T> void bar(T&& f) = delete;
这样做......
int main() {
auto i=2;
auto d=2.0;
auto f=2.0f;
foo(i); // prevented
foo(d); // OK
foo(f); // prevented
auto uil = 3ull;
auto ul = 3ul;
auto u = 3u;
bar(i); // prevented
bar(d); // prevented
bar(f); // prevented
bar(uil); // prevented
bar(ul); // prevented
bar(u); // OK
}
现在,如果我使用已删除的模板或删除的非模板功能,或者是否有重要的情况,这些情况只是一个问题吗?我发现删除的模板更明确,防止所有T ,但另一方面,当使用此模式与构造函数时;转发构造函数have their issues。
如果是模板化版本,最好是删除模板const T&
吗?
答案 0 :(得分:3)
首先,我认为值得注意的是,非模板版本会阻止大多数情况,因为它们会导致两个重载之间存在歧义,而模板版本通过提供比非模板重载更好的匹配来实现。因此,模板版本生成的错误消息将更加清晰,沿着“你试图调用这个被删除的函数”,而不是“我无法在这两者之间做出决定,哪一个做你真的想要吗?“从这个角度来看,模板版本看起来更好。
但是,有些情况会有不同的行为。
一个模糊的情况类似foo({f});
,模板版本不会阻止这种情况,因为初始化列表使参数成为非推导的上下文,因此模板的演绎失败,只留下非模板超载。
出于类似的原因,模板版本会阻止foo({i});
,但不会阻止foo({3});
(3
是常量,因此转换为double
不是缩小转化率,因为3
适合double
并在转换回时生成相同的值。非模板版本阻止了两者,因为它们都是模棱两可的。
另一个案例:
enum E : unsigned { };
int main()
{
E e{};
bar(e);
}
模板版本通过提供最佳过载来防止这种情况。非模板的不是,因为E
到unsigned int
是促销,这比E
到int
更好,这是转化。
类似的问题会出现在平台上,例如,short
的大小与int
的大小相同,也适用于来自char16_t
,char32_t
或{{的转化的平台1}},取决于他们特定于平台的表示。
尽管对于问题的背景可能不那么有趣,但另一个区别似乎是:
wchar_t
模板版本通过提供最佳重载来防止这种情况,而非模板版本则不会(调用struct A
{
operator double() { return 7.0; }
};
int main()
{
A a{};
foo(a);
}
)。