如何确保调用显式构造函数并允许大括号初始化?



考虑下面的基本程序。它typedef int Number,目的是允许未来的开发人员将其更改为当时最有意义的精度(甚至使用CGAL的数字类型之一(。

你可以看到一个Point被描述为一对Number,而一个LineSegment被描述为两个Point

我想实现两个目标:

  1. 如果用户尝试使用显式Number以外的任何内容实例化Point,则应触发编译时警告。我认为这就是explicit关键字的作用。相反,正如您在p3的实例化中看到的那样,我们静默地允许将float转换为int
  2. 如果用户想要用大括号初始化具有常量值的LineSegment,只要这些值Number就可以。

我的问题:

  1. 我是否误解了应该如何使用explicit
  2. 期望编译器捕获此内容是不可行的吗?换句话说,它是否需要在运行时的编程逻辑中?
typedef int Number;
class Point
{
public:
explicit Point(Number n1, Number n2)
: x(n1), y(n2)
{}
Number x;
Number y;
};
class LineSegment
{
public:
LineSegment(Point p1, Point p2)
: start(p1), end(p2)
{}
Point start;
Point end;
};
int main()
{
// Works as expected
Point p1(10, 10);
Point p2(20, 20);
LineSegment s1(p1, p2);
// How can I trigger a compiler error here?
Point p3(10.5, 11.5);
// Why can't a do the following?
//LineSegment s2({30, 30}, {40, 40});
return 0;
}

是的,你误解了explicit. 构造函数上的explicit阻止构造函数被用作隐式用户定义转换,例如,当某些内容传递给期望您的类类型作为参数的函数时。正如@JohannesSchaub在问题注释中指出的那样,在构造函数上使用explicit实际上是阻止LineSegment s2({30, 30}, {40, 40});出于这个确切原因进行编译的原因。

explicit不会阻止构造函数自己的参数中的隐式转换。

使用括号中的初始值设定项语法时无法避免隐式转换,但可以通过使构造函数成为模板函数并检查其模板参数类型来获取传递的实际类型:

template<typename T1, typename T2>
Point(T1 n1, T2 n2)
: x(n1), y(n2)
{
static_assert(std::is_same_v<T1,Number> && std::is_same_v<T2,Number>
, "Construct Point with Number arguments!");
}

需要#include<type_traits>才能std::is_same_v和C++17。(在 C++17 之前,您需要使用std::is_same<...>::value

或者,如果您不希望使用错误的类型导致硬错误,则可以使用std::enable_if而不是static_assert

相关内容

  • 没有找到相关文章

最新更新