Prefer one type convert into another through implicit constructor or implicit conversion operator?
Asked Answered
P

1

8

Assume we have procedure void f(X2);. Further assume we have types X1 and X2 that do not share an inheritance hierarchy.

We want to call it like this: f(X1{}) and have X1 implicitly convert into X2. We have two options of achieving this:

struct X2 {};
struct X1 { operator X2(); };
struct X1 {};
struct X2 { X2(const X1&); };

Implementation-wise there are practical differences between the two with the order things are defined and how private data is accessed.

But from a user perspective, are there any cases where these two approaches will behave differently? If so, which of the two is preferable?

This question remains the same even if we mark both explicit. One difference that arises then is that conversion through the constructor is only available in the former case but static_cast works with either.

Pantoja answered 26/11, 2022 at 13:48 Comment(6)
One practical difference is "does X2 know about X1?" versus "does X1 know about X2?".Alis
Prefer explicit conversion. Implicit conversions can lead to unpleasant surprises later on. C++ Core Guidelines - C.164: Avoid implicit conversion operatorsBeaded
@RichardCritten the example in the guidelines you linked is interesting because it discourages the implicit conversion from C-string to std::string but the other way around is fine and part of the standard library. The use case I am facing is very similar to thisPantoja
If the conversion involves the construction of a new object use a constructor.Sandell
@Sandell what is the reasoning behind your answer? It would also be possible to extract the relevant data and call a public constructor of X2 with that dataPantoja
You might also like to read C++ Core Guidelines - C.46: By default, declare single-argument constructors explicitBeaded
E
0

Both approaches are functionally equivalent, so as to let you define both kinds of conversion even if one of the types is a class you cannot modify, or is not a class at all, like a fundamental type.

In the implicit case, both options will let you call f(X1{}), in the explicit case they will both allow f(X2{X1{}}). The second option simply uses the converting constructor directly, while the first option uses X2's copy or move constructor, which takes its argument from the conversion operator. The actual copy or move operation will be elided, so there is no difference in performance.

Edelstein answered 2/12, 2022 at 0:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.