Multiple Implicit Conversions on Custom Types Not Allowed

Multiple implicit conversions on custom types not allowed?

There is no multi-step user-defined implicit conversion.

int -> bool -> A

is allowed because the int->bool conversion isn't user-defined.

12.3 Conversions [class.conv]

1 Type conversions of class objects can be specified by constructors
and by conversion functions. These conversions are called user-defined
conversions and are used for implicit type conversions (clause 4), for
initialization (8.5), and for explicit type conversions (5.4, 5.2.9).

2 User-defined conversions are applied only where they are unambiguous
(10.2, 12.3.2). Conversions obey the access control rules (clause 11).
Access control is applied after ambiguity resolution (3.4).

3 [ Note:
See 13.3 for a discussion of the use of conversions in function calls
as well as examples below. —end note ]

4 At most one user-defined
conversion (constructor or conversion function) is implicitly applied
to a single value.

Implicit casting limitations

Unlike standard conversions, implicit conversion sequence can consist of one user-defined conversion at most.

Implicit conversion sequence consists of the following, in this order:

1) zero or one standard conversion sequence;

2) zero or one user-defined conversion;

3) zero or one standard conversion sequence.

For your 1st code snippet, given B("example");, two user-defined conversions are required; const char* to std::string and std::string to A.

As the workaround, you can add another conversion constructor as you tried, or add explicit conversion to construct B, e.g.

B(A("example"));

What rules govern use of multiple user-defined conversions between types?

tldr; The code is ill-formed, MSVC is wrong to accept it. Copy-initialization is different from direct-initialization. The layman explanation is that the initialization of yoursBreaks would involve two user-defined conversions (MyString --> const char* --> YourString), whereas direct-initialization involves one user-defined conversion (MyString --> const char*), and you are allowed at most one user-defined conversion. The standardese explanation which enforces that rule is that [over.best.ics] doesn't allow for user-defined conversions in the context of copy-initialization of a class type from an unrelated class type by way of converting constructor.


To the standard! What does:

YourString yoursBreaks = mys;

mean? Any time we declare a variable, that's some kind of initialization. In this case, it is, according to [dcl.init]:

The initialization that occurs in the = form of a brace-or-equal-initializer or condition (6.4), as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.6.1), is called copy-initialization.

Copy-initialization is anything of the form T var = expr; Despite the appearance of the =, this never invokes operator=. We always goes through either a constructor or a conversion function.

Specifically, this case:

If the destination type is a (possibly cv-qualified) class type:

— If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same
class as the class of the destination, [...]

— Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the
cv-unqualified version of the source type is the same class as, or a derived class of, the class of the
destination, [...]

— Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that
can convert from the source type to the destination type or (when a conversion function is used) to
a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through
overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is
ill-formed.

We fall into that last bullet. Let's hop over into 13.3.1.4:

— The converting constructors (12.3.1) of T are candidate functions.

— When the type of the initializer expression is a class type “cv S”, the non-explicit conversion functions of S and its base classes are considered. When initializing a temporary to be bound to the first parameter
of a constructor where the parameter is of type “reference to possibly cv-qualified T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T”,
explicit conversion functions are also considered. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions. Conversion functions that return “reference to X” return lvalues or xvalues, depending on the type
of reference, of type X and are therefore considered to yield X for this process of selecting candidate functions.

The first bullet point gives us the converting constructors of YourString, which are:

YourString(const char* );

The second bullet gives us nothing. MyString does not have a conversion function that returns YourString or a class type derived from it.


So, okay. We have one candidate constructor. Is it viable? [over.match] checks reliability via:

Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.

and, in [over.best.ics]:

A well-formed implicit conversion sequence is one of the following forms:

— a standard conversion sequence (13.3.3.1.1),

— a user-defined conversion sequence (13.3.3.1.2), or

— an ellipsis conversion sequence (13.3.3.1.3).

However, if the target is

the first parameter of a constructor or

— the implicit object parameter of a user-defined conversion function

and the constructor or user-defined conversion function is a candidate by

— 13.3.1.3, when the argument is the temporary in the second step of a class copy-initialization,

13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases), or

— the second phase of 13.3.1.7 [...]

user-defined conversion sequences are not considered. [ Note: These rules prevent more than one user-defined
conversion from being applied during overload resolution, thereby avoiding infinite recursion. —end note ] [ Example:

struct Y { Y(int); };
struct A { operator int(); };
Y y1 = A(); // error: A::operator int() is not a candidate

struct X { };
struct B { operator X(); };
B b;
X x({b}); // error: B::operator X() is not a candidate

—end example ]

So even though there is a conversion sequence from MyString to const char*, it is not considered in this case, so this constructor is not viable.

Since we don't have another candidate constructor, the call is ill-formed.


The other line:

YourString yoursAlsoWorks(mys);

is called direct-initialization. We call into the 2nd bullet point of the three in the [dcl.init] block I quoted earlier, which in its entirety reads:

The applicable constructors are enumerated (13.3.1.3),
and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

where 13.3.1.3 indicates that constructors are enumerated from:

For direct-initialization
or default-initialization that is not in the context of copy-initialization, the candidate functions are all the constructors of the class of the object being initialized.

Those constructors are:

YourString(const char* )        // yours
YourString(YourString const& ) // implicit
YourString(YourString&& ) // implicit

To check the viability of the latter two functions, we re-perform overload resolution from a copy-initialization context (which fails as per the above). But for your YourString(const char*), it's straightforward, there is a viable conversion function from MyString to const char*, so it's used.

Note that there is one single conversion here: MyString --> const char*. One conversion is fine.

Custom Implicit Conversion in Java

You can not overload operators with Java.
Add a method to your class of choice to perform the conversion.
For example:

public class Blammy
{
public static final Blammy blammyFromHooty(final Hooty hoot)
{
Blammy returnValue;

// convert from Hooty to Blammy.

return returnValue;
}
}

Edit

  • Only built-in types have implicit conversions.
  • Explicit conversion (in java) is called a cast. for example, int q = 15; double x = (double) q;
  • There can never be implicit conversions of your custom types.
  • extends and implements are not conversion.
  • All implicit conversions are for primitive types.

Interfaces, Inheritance, Implicit operators and type conversions, why is it this way?

You have basically asked the compiler to do this:

  1. I have this: DbRecurPatt
  2. I want this: IRecurrencePattern
  3. Please figure out a way to get from point 1. to point 2.

The compiler, even though it may only have one choice, does not allow you to do this. The cast operator specifically says that DbRecurPatt can be converted to a RecurrencePattern, not to a IRecurrencePattern.

The compiler only checks if one of the two types involved specifies a rule on how to convert from one to the other, it does not allow intermediary steps.

Since no operator has been defined that allows DbRecurPatt to be converted directly to IRecurrencePattern, the compiler will compile this as a hard cast, reinterpreting the reference as a reference through an interface, which will fail at runtime.

So, the next question would be this: How can I then do this? And the answer is you can't.

The compiler does not allow you to define a user-defined conversion operator to or from an interface. A different question here on Stack Overflow has more information.

If you try to define such an operator:

public static implicit operator IRecurrencePattern(DbRecurPatt obj)
{
return new RecurrencePattern() { Data = $"{obj.Name} - {obj.Description}" };
}

The compiler will say this:

CS0552

'DbRecurPatt.implicit operator IRecurrencePattern(DbRecurPatt)': user-defined conversions to or from an interface are not allowed

In Swift is it possible to create implicit conversions for your class types?

Swift doesn't support implicit conversion - the closest equivalent is done through extra constructors. So to convert a Double to an Int:

let i: Int = Int(myDouble)

In your case, you could define an extension to Int to create a new init that takes fauxInit as a parameter:

extension Int {
init(_ myFaux: FauxInt) {
self = myFaux.value
}
}

let fauxInt = FauxInt(14)
let realInt = Int(fauxInt)

Scala: Multiple implicit conversions with same name

The implicits just have to be available as a simple name, so you can rename on import.

Just to verify:

scala> import A._ ; import B.{ Imp => BImp, _ }
import A._
import B.{Imp=>BImp, _}

scala> 3.myPrint
3

Implicit conversion of Int* to custom class in function call

You can implicitly convert only 1 value to another 1 value, you cannot use conversion to "overload" unary method into variadic method.

You could implement conversions so that

foo(Seq(1, 2, 3))

would become

foo(MySeq.seq2MySeq(Seq(1, 2, 3))

but to be able to use

foo(1, 2, 3)

you would have to overload the method e.g.

def foo(ints: Int*) = foo(MySeq(ints))

Implicit operator has error when converting to custom defined type

You cannot declare an implicit conversion to one of your own base classes. System.Object is always your (direct or indirect) base class. Through inheritance there's already an implicit conversion to the base class. So how could there be two implicit conversions? The C# spec has a good reson to disallow this!



Related Topics



Leave a reply



Submit