简体   繁体   中英

Function-style casts considered harmful?

I understand that generally C-style casts are considered harmful. So, given a class Widget

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

I could call a function like this:

functionThatTakesAWidget((Widget) gizmo);

using a C-style cast. Or I could write it using a function-style cast:

functionThatTakesAWidget(Widget(gizmo));

but this is exactly equivalent. So no better or worse. And If I really wanted to avoid a C-style cast I could write:

functionThatTakesAWidget(static_cast<Widget>(gizmo));

But given a class Doohickey :

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

I can't avoid writing:

functionThatTakesADoohickey(Doohickey(left_gizmo, right_gizmo));

I assume this is not considered a 'cast' at all? But what is the difference between this and a function-style cast? Why is this OK but a function-style cast is not?

The reasons for avoiding C-Style casts are:

  • Being explicit about what kind of casting happens, thus making sure no other type actually does.
  • Making casts easier findable.

While the function-style cast looks safer, it's actually a C-style cast in disguise, so it does not really add anything.

The driving reason behind being explicit is letting the compiler assert your assumptions, instead of silently doing (sometimes) the wrong thing.
IMHO, some purists go to far in their zeal though.

I wouldn't call Widget(gizmo) a "C-style cast". In C you can't cast to a struct type anyway. The main problem with "C-style casts" is when a pointer or reference cast is happening. Then the C-style cast can be ambiguous or cause undefined behaviour.

A value conversion is a different concept to a reference cast, in my view, and you don't need to use the same conventions for both.

The code is creating a temporary Widget , passing gizmo to the constructor. I think the static_cast version is misleading, and the Widget(gizmo) is fine.

BTW you could make functionThatTakesADoohickey overloaded to take a Gizmo , or a std::initializer_list<Gizmo> .

I believe:

C c;
(A)c

calls the casting operator "C::operator A ()". Which may be called when a C is provided, but an A is required. For example:

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

Also,

A(c)

calls the constructor: "A::A(const& C)".

I believe that if you don't define the cast operator, C++ may end calling the constructor implicitly to get the A. Sometimes this is a big disaster (let's say this constructor formats your hard drive and files for a divorce). So there's two remedies:

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

and also

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

Because "function-style" casts may file for divorce (by calling the constructor), they're considered harmful.

There's nothing wrong with:

f(c1,c1)

to get a A. People typically wouldn't think of it as a 'cast' which usually means 'turn this type of object into that type'.

Typically you wouldn't write:

void AA(const& C) {...} // function

to get

d = A(c)

because its confusing. (is A class or a function?)

And you'd prefer not to write:

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

simply because this functionality is better off encapsulated in the A class. But its not otherwise particularly harmful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM