函数风格的强制类型转换是否有害?

3

我了解通常认为 C 风格的强制类型转换是有害的。因此,对于一个类 Widget

class Widget{
public:
  explicit Widget(Gizmo gizmo);
};

我可以像这样调用一个函数:

functionThatTakesAWidget((Widget) gizmo);

可以使用C风格转换,也可以使用函数风格转换:

functionThatTakesAWidget(Widget(gizmo));

但是这完全等效。所以没有更好或更糟。如果我真的想避免 C 风格的转换,我可以写成:

functionThatTakesAWidget(static_cast<Widget>(gizmo));

假设有一个名为Doohickey的类:

class Doohickey {
public:
  Doohickey(Gizmo left_gizmo, Gizmo right_gizmo);
};

我不得不写下这句话:
functionThatTakesADoohickey(Doohickey(left_gizmo, right_gizmo));

我假设这不被认为是一个“转换”?但这与函数样式的转换有什么区别?为什么这个可以,而函数样式的转换不行?


函数式转换也不是一种转换,它实际上是一个构造函数或类型转换运算符的调用。 - user207421
@EJP “(Widget) gizmo”与“Widget(gizmo)”完全等价,这是一个类型转换,对吗? - user3502661
C风格的转换和构造函数或类型转换之间存在差异。C风格的转换是从C编程语言中继承而来的,它会将一个变量位置重新解释为不同的类型,并告诉编译器特定的变量或内存位置是特定的类型。与C++提供转换方法或编译器可以找到一系列执行转换的方法不同,C风格的转换没有进行转换。由于这种差异,C风格的转换要不安全得多。 - Richard Chambers
3个回答

1
避免使用C风格的转换的原因有:
  • 明确所进行的类型转换,以确保没有其他类型实际上进行了转换。
  • 使转换更容易被找到。
虽然函数式转换看起来更安全,但实际上它是伪装成C风格转换,因此它并没有真正添加任何内容。
明确的驱动原因是让编译器断言您的假设,而不是默默地执行(有时)错误的操作。
在我看来,一些纯粹主义者在他们的热情中走得太远了。

4
“一个简单类型说明符(7.1.6.2)或类型名称说明符(14.6)后跟括号表达式列表构造指定类型的值,给定表达式列表。如果表达式列表是单个表达式,则类型转换表达式在定义性和(如果定义了含义)中等同于相应的转换表达式(5.4)。这对我来说听起来并不更加受限。” - Jerry Coffin
@Jerry:我真的认为使用函数式转换至少有一些伪理由。测试过后发现实际上并没有。 - Deduplicator

0

我相信:

C c;
(A)c

调用转换运算符"C::operator A()"。当需要提供C,但需要A时,可以调用该运算符。例如:

void f(A const&) { ... }
f(c);

另外,

A(c)

调用构造函数:"A::A(const& C)"。

我相信如果您不定义转换运算符,C++可能会隐式地调用构造函数来获取A。有时这是一个巨大的灾难(比如说这个构造函数格式化了您的硬盘并为离婚做准备)。因此,有两种解决方法:

class A {
public:
   explicit A(const& C);  // Don't call this when asking to cast to an A.
}

而且

static_cast<A>(C);  // Really, just call the casting operator, don't implicitly cast.

因为“函数式”转换可能会触发构造函数而导致错误,所以它们被认为是有害的。

以下内容没有问题:

f(c1,c1)

为了得到一个A,人们通常不会将其视为“转换”,这通常意味着“将此类型的对象转换为那种类型”。

通常你不会写:

void A A(const& C) {...} // 函数

来获得

d = A(c)

因为这很令人困惑。(A是类还是函数?)

而且你更愿意不写:

void A toA(const& C) {...}

只是因为这个功能最好封装在A类中。但它本身并没有特别有害。


0

我不会把Widget(gizmo)称为“C样式转换”。在C中,你无法将其转换为结构体类型。 “C风格转换”的主要问题是在发生指针或引用转换时。然后,C样式转换可能会导致歧义或未定义的行为。

在我看来,值转换是一个不同的概念,与引用转换不同,并且您不需要为两者使用相同的约定。

该代码正在创建一个临时的Widget,并将gizmo传递给构造函数。我认为static_cast版本是令人误解的,而Widget(gizmo)则很好。

顺便说一句,您可以将functionThatTakesADoohickey重载为接受Gizmostd::initializer_list<Gizmo>之一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接