我有:
class C
{
C(long){...};
C(double){...};
:
}
不幸的是,
C c{5}; // error! ambiguous overload
(这是相当可怕的,不是吗?一个整数类型肯定会有利于构造函数采用更高精度的积分参数。)
如何正确地将积分参数和浮点参数正确地转发到它们各自的构造函数?
编辑:也许我过分简化了问题。它最初来自this enquiry。我正在包装Python原语,例如Float Long String,初始化转发到正确的原语非常重要。同时,由于这是一个通用的包装器,我不希望消费者不必担心类型转换,以避免内部陷阱。Mike Seymour指出,SFINAE提供了处理此问题的技术。
非常感谢FreeNode C ++频道上的doug64k提供以下解决方案:
http://ideone.com/QLUpu2 http://ideone.com/TCigR3 http://ideone.com/oDOSLH
当我明天上路时,我会尝试将这些作为答案。
答案 0 :(得分:3)
如何正确地将积分参数和浮点参数正确地转发到它们各自的构造函数?
使用:
C c1{5L};
C c2{5.0};
这太可怕了,不是吗?积分类型肯定有利于构造函数采用更高精度的积分参数
该标准遵循算法来确定哪个过载函数是最佳匹配。除其他外,它排名"促销"高于"转化"。这意味着"整体推广"排名高于"积分转换","浮点促销"排名高于"浮点转换"。然而,"积分转换"排名不高于"浮点转换"这是 13.3.3.1.1标准转换序列
部分中的表格
标准解决了"积分促销","积分转换","浮点促销"和"浮点转换"可以在 4标准转换部分下使用。出于本答案的目的,可以说int
可以转换到long
未提升。 int
也可以转换到double
。这就解释了为什么当参数类型为int
时,编译器无法消除重载之间的歧义。
答案 1 :(得分:2)
我只是保持原样,让用户明确要求进行哪种类型转换。如果您确实希望根据您描述的非标准规则允许隐式转换,则可以使用SFINAE。像这样的东西会起作用:
#include <iostream>
#include <type_traits>
struct C {
// Constructor for integer types
template <typename T>
C(T, typename std::enable_if<std::is_integral<T>::value, T>::type=0)
{std::cout << "integral\n";}
// Constructor for floating-point types
template <typename T>
C(T, typename std::enable_if<std::is_floating_point<T>::value, T>::type=0)
{std::cout << "floating\n";}
};
int main() {
C c1{5}; // prints "integral"
C c2{5.0}; // prints "floating"
}
这种乱码,以及那些习惯于通常的隐式转换规则的混淆,是否值得在显式转换中保存是一个意见问题。
答案 2 :(得分:1)
您可以使用模板构造函数将未声明的类型重定向到您选择的构造函数。
假设您希望long
成为您的默认设置。使用SFINAE,您可以检查类型T
是否能够作为long
转换,然后将其传递给长构造函数!
class C
{
public:
C(long l){ std::cout << "long constructor" << std::endl; };
C(double d){std::cout << "double constructor" << std::endl; };
// default constructor which passes values to long
template <typename T,
typename std::enable_if<std::is_convertible<long, T>::value, int>::type = 0>
C(T t) : C(long(t)){};
};
int main() {
C c1(5);
C c2(5.0f);
C c3(5.0L);
C c4(5.0);
return 0;
}
输出:
long constructor
long constructor
long constructor
double constructor