struct A {};
struct B
{
B (A* pA) {}
B& operator = (A* pA) { return *this; }
};
template<typename T>
struct Wrap
{
T *x;
operator T* () { return x; }
};
int main ()
{
Wrap<A> a;
B oB = a; // error: conversion from ‘Wrap<A>’ to non-scalar type ‘B’ requested
oB = a; // ok
}
When oB is constructed then Why B::B(A*) is NOT invoked for Wrap<T>::operator T () ? [Note: B::operator = (A*) is invoked for Wrap<T>::operator T () in the next statement]
The problem is that the number of user-defined conversions that are invoked implicitly is limited (to 1) by the Standard.
B ob = a;
implies two user conversions:
on a: Wrap<A>::operator A*() should be called
on the result: B::B(A*) should be called
#James Kanze's explanation: this syntax is called "copy initialization", effectively equivalent to B ob = B(a) (with the copy being elided most of the time). This is different from B ob(a) which is a "direct initialization" and would have worked.
if you explicitly qualify any of this, it will work, for example:
B ob = B(a);
On the other hand, for the second case there is no issue:
ob = a;
is short-hand for:
ob.operator=(a);
And thus only one user-defined conversion is required, which is allowed.
EDIT:
Since it's been required in a comment (to Kirill's answer) we can take a guess at the motive.
Chained conversions could be long, very long, and therefore:
could surprise users -- implicit conversions may already be surprising as it is...
could lead to an exponential search of the possibilities (for the compiler) -- it would need to go from both ends, trying to check all possible conversions, and somehow "join" the two (with the shortest path possible).
Furthermore, as long as there is more than 1 conversion, you run into the risk of having cycles, which would have to be detected (even though diagnostic would probably not be required, and be subject to Quality Of Implementation).
So, since a limit is necessary to avoid infinitely long searches (it could have been left unspecified, with a minimum required), and since beyond 1 we may have new issues (cycles), then 1 seems as good a limit as any after all.
It's because you're using "copy initialization". If you write the
declaration of oB:
B oB(a);
, it should work. The semantics of the two initializations are
different. For B oB(a), the compiler tries to find a constructor
which can be called with the given arguments. In this case, B::B(A*)
can be called, because there is an implicite conversion from Wrap<A>
to A*. For B oB = a, the semantics are to implicitly convert a to
type B, then use the copy constructor of B to initialize oB. (The
actual copy can be optimized out, but the legality of the program is
determined as if it weren't.) And there is no implicit conversion of
Wrap<A> to B, only of Wrap<A> to A*.
The assignment works, of course, because the assignment operator also
takes a A*, and so the implicit conversion comes into play.
The Standard doesn't allow chained implicit conversion. If it was allowed, then you could have written such code:
struct A
{
A(int i) //enable implicit conversion from int to A
};
struct B
{
B(const A & a); //enable implicit conversion from A to B
};
struct C
{
C(const B & b); //enable implicit conversion from B to C
};
C c = 10; //error
You cannot expect that 10 will convert to A which then will convert to B which then converts to C.
B b = 10; //error for same reason!
A a = 10; //okay, no chained implicit conversion!
B ba = A(10); //okay, no chained implicit conversion!
C cb = B(A(10)); //okay, no chained implicit conversion!
C ca = A(10); //error, requires chained implicit conversion
The same rule applies for implicit conversion that invokes operator T() implicitly.
Consider this,
struct B {};
struct A
{
A(int i); //enable implicit conversion from int to A
operator B(); //enable implicit conversion from B to A
};
struct C
{
C(const B & b); //enable implicit conversion from B to C
};
C c = 10; //error
You cannot expect that 10 will convert to A which then will convert to B(using operator B()) which then converts to C. S
Such chained implicit conversions are not allowed. You've to do this:
C cb = B(A(10); //okay. no chained implicit conversion!
C ca = A(10); //error, requires chained implicit conversion
This is because C++ Standard allows only one user-defined conversion. According to §12.3/4:
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single
value.
B oB = a; // not tried: ob( a.operator T*() ), 1 conversion func+1 constructor
oB = a; // OK: oB.operator=( a.operator T*() ), 1 conversion func+1 operator=
As a workaround you can use explicit form of calling the constructor:
B oB( a ); // this requires only one implicit user-defined conversion
Related
I have some difficulty to understand mutual conversions, it is explained in C++ Primer 5 ed.:
"In the following example, we’ve defined two ways to obtain an A from a B: either by using B’s conversion operator or by using the A constructor that takes a B:// usually a bad idea to have mutual conversions between two class types:
struct B;
struct A {
A() = default;
A(const B&); // converts a B to an A
// other members
};
struct B {
operator A() const; // also converts a B to an A
// other members
};
A f(const A&);
B b;
A a = f(b); // error ambiguous: f(B::operator A())
// or f(A::A(const B&))
Because there are two ways to obtain an A from a B, the compiler doesn’t know which conversion to run; the call to f is ambiguous. This call can use the A constructor that takes a B, or it can use the B conversion operator that converts a B to an A. Because these two functions are equally good, the call is in error.
If we want to make this call, we have to explicitly call the conversion operator or the constructor:
A a1 = f(b.operator A()); // ok: use B's conversion operator
A a2 = f(A(b)); // ok: use A's constructor
Note that we can’t resolve the ambiguity by using a cast—the cast itself would have
the same ambiguity."
But when I defined the required members and f I don't get any error! his explanation looks so logical but I don't know why the code runs fine?!
But when I defined the required members and f I don't get any error!
The shown program is ill-formed for the reasons described in the book. If a compiler fails to issue a diagnostic message, then the compiler does not conform to the standard.
I am using GCC.
You can ask GCC to try to conform to the C++ standard using the -pedantic option. Unfortunately, it will otherwise enable some language extensions by default.
I cannot undestand why a copy constructor is called instead of a compile error.
I have declare two classes A B , which are independent . A is not based/derived of B. The only connection between them is that in B I use an operator that converts B to A
I have defined an operator = that gets as an argument a const reference of B.
In main I have written the following instnace of B = instance of A. I expect that a compile error will be generated. But the operator = is called
class A {};
class B {
public:
// conversion from A (constructor):
B()
{
cout << "1." << endl;
}
B(const A& x)
{
cout << "4." << endl;
}
// conversion from A (assignment):
B& operator= (const B& x)
{
cout << "3." << endl;
return *this;
}
// conversion to A (type-cast operator)
operator A() {
cout << "2." << endl;
return A();
}
};
int main()
{
A foo;
B bar; // calls constructor
bar = foo; // calls assignment
//foo = bar; // calls type-cast operator
char c;
c = getchar();
return 0;
}
I expect compile error. But the following sequence is printed
1 4 3 .
I hardly undestand how the copy constructor is called and why the operator = does not generate problem
Thank you
You have an implicit constructor
B::B(const A&)
which performs the conversion that you don't want. You can change the signature to
explicit B(const A&);
to trigger a compilation error. Note that it's good practice to mark constructors with one argument explicit by default (there is a clang-tidy check for it, too), just to avoid such conversions by accident (IMHO, it would even be better if constructors that can be constructed with a single argument would be explicit by default with the ability to make the implicit).
It's not a copy constructor that you have, it's a conversion constructor. The conversion constructor is called because you can turn a A into a B.
Do this:
explicit B(const A& x)
And now you will forbid the implicit conversion from A to B.
When you do:
bar = foo;
The compiler looks for sensible operations and allows for one conversion at most. It can use the copy assignment from B, and it knows it can create a B out of an A (because the constructor is not explicit), so it does this silently.
As #lubgr said, clang-tidy has a rule to check these and this is part of the C++ core guidelines.
I expect the following compile error will be generated
no match for 'operator=' ... note: no known conversion for argument 1 from 'A' to 'const B&'
There is no reason to expect such error, because there is a known conversion from A to const B&. B has a converting constructor for A:
B(const A& x)
The only connection between them is that in B I use an operator that converts B to A
... and the second connection between them is that in B, you use a constructor that converts A to B.
You are right that this a conversion cunstroctor. But why this called when the assignement bar = foo takes place.
Because the given operand of the assignment has the type A, while the declared argument type is const B&.
In cases where the argument type does not match the declaration, the compiler checks whether there exists an implicit conversion sequence which could be used to convert the argument. As, A is implicitly convertible to B, such implicit conversion sequence exists and will be used.
I have the situation where I have a class A, that provides a constructor for an integral type, and a class B that provides a implicit conversion operator for the same integral type. However, if I call a function accepting a reference to class A with an instance of class B, the compilation fails. I would have expected an implicit conversion of class B to the type accepted by the constructor of class A. Of course, if I add a constructor to A accepting class B, everything is fine. Is this behavior intended? Please checkout the example below.
#include <iostream>
class B
{
public:
B() = default;
B(const std::uint8_t &val) : mVal(val) {}
std::uint8_t get() const { return mVal; }
operator std::uint8_t() const { return mVal; }
private:
std::uint8_t mVal;
};
class A
{
public:
A() = default;
A(const std::uint8_t &val) : mVal(val) {}
// No problem if this exists
// A(const B &b) : mVal(b.get()) {}
std::uint8_t get() const { return mVal; }
private:
std::uint8_t mVal;
};
void func(const A &a)
{
std::cout << static_cast<int>(a.get()) << std::endl;
}
int main(int, char*[])
{
std::uint8_t val = 0xCE;
A a(val);
B b(val);
func(val); // fine
func(a); // fine
func(b); // error
}
There is a rule in C++ that no implicit conversion will use two user-defined conversions.
This is because such "long-distance" conversions can result in extremely surprising results.
If you want to be able to convert from anything that can convert to a uint8_t you can do:
template<class IntLike,
std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true
>
A( IntLike&& intlike ):A( static_cast<std::uint8_t>(std::forward<IntLike>(intlike)) )
{}
or you could cast your B to an uint8_t at the point you want to convert to an A.
You can do a similar thing in B where you create a magical template<class T, /*SFINAE magic*/> operator T that converts to anything that can be constructed by an uint8_t.
This obscure code:
std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true
exists to make sure that the overload is only used if the type we are converting from has the properties we want.
The first enable_if clause states that we only want things that can convert to uint8_t. The second states we don't want this constructor to be used for the type A itself, even if it passes the first.
Whenever you create a forwarding reference implicit constructor for a type, that second clause is pretty much needed or you get some other surprising issues.
The technique used is called SFINAE or Substitution Failure Is Not An Error. When a type IntType is deduced and those tests fail, there is substitution failure in those clauses. Usually this would cause an error, but when evaluating template overloads it is not an error because SFINAE; instead, it just blocks this template from being considered in overload resolution.
You are only allowed one user defined conversion when implicitly creating a object. Since func needs an A you would have a user defined conversion to turn B into a std::uint8_t and then another user defined conversion to turn that std::uint8_t into an A. What you would need is a operator A in B or a constructor in A that takes a B if you want it to happen implicitly. Otherwise you can just explicitly cast so you only need a single implicit one like
func(static_cast<std::uint8_t>(b)); // force it to a uint8_t
// or
func({b}); // make b the direct initializer for an A which will implicitly cast
// or
func(A{b}); same as #2 above but explicitly sating it
Is this behavior intended?
Yes, it is intended.
An implicit conversion sequence can have at most one user-defined conversion (constructor or conversion function).
Standard says (emphasis mine):
[over.ics.user]
A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-
defined conversion (15.3) followed by a second standard conversion sequence. ...
In order for a user defined type (a class) to be implicitly convertible to another, there must be a constructor or conversion operator directly to that type. Implicit conversion (from user defined type to another) is not possible through an intermediate type.
You could use explicit conversion instead.
Lets concider following code:
class A{
public:
A(int x){}
};
class B{
public:
B(A a){};
};
int main() {
B b = 5;
return 0;
}
And while compiling the compiler complains that:
/home/test/main.cpp:80: candidate constructor not viable: no known conversion from 'int' to 'A' for 1st argument
I don't want to create B(int) constructor - I would love the compiler to implicit convert this int to A object.
EDIT:
Direct initialisation works like this:
B b(5);
Is it possible to use the assigment operator instead?
Just to be clear:
B b = 5;
is "copy initialisation" not assignment. (See http://www.gotw.ca/gotw/036.htm).
In this case, you are asking the compiler to perform two implicit user-defined conversions first, i.e. int -> A, A -> B before a temporary B object is passed to the copy constructor for B b. The compiler is allowed to elide the temporary object but semantically you are still asking the language to make two jumps across types.
All implicit behaviour in programming languages is inherently scary. For the sake of a little syntactic sugar, we are asking c++ to "do some magic to make it just work". Unexpected type conversions can wreck havoc in large complex programmes. Otherwise, every time you wrote a new function or a new class, you would have to worry about all the other types and functions it could affect, with the side -effects rippling across your code.
Would you really want implicit conversions from int -> apple -> horse -> horse_power -> aeroplane?
For that reason, c++ only allows a single implicit user-defined conversion:
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).
4 At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
You are better off either with an explicit cast or "direct initialisation" both of which make it clear to the compiler and collaborators exactly what you are trying to do. Either the traditional or the new uniform initialisation syntax works:
B b(5);
B b{5};
B b = {5};
Use direct initialisation instead:
B b(5);
You can use a converting constructor that is constrained on conversions to A.
class B {
public:
// It doesn't hurt to keep that one
B(A a){};
template<
typename T
, EnableIf<std::is_convertible<T, A>>...
>
B(T&& value)
{
// How to use the value
A a = std::forward<T>(value);
}
};
// Now B b = foo; is valid iff A a = foo; is, except for a few exceptions
Make sure you understand the purpose of the EnableIf constraint. If you do not use it, you will face gnarly compilation errors, or worse: no errors at all. You should also carefully consider if making B convertible from potentially lots of types is at all worth it. Implicit conversions tend to make a program harder to understand, and that can quickly outweigh the apparent benefits they give.
Given the following two constructor signatures, should it be possible to construct a Couple with Couple("George", "Nora")? My compiler complains with the error shown below. If I call it with Couple(std::string("George"), std::string("Nora")) it compiles OK. I'm guessing there's an issue with the implicit casting which surprises me as I though char* to string would be fine.
class Person
{
public:
Person(const std::string& name);
};
class Couple
{
public:
Coordinate(const Person& p1, const Person& p2, const Optional<Person>& = Optional<Person>());
};
TestCouple.cpp:69: error: no matching function for call to `Couple::Couple(const char[7], const char[5])'
TestCouple.h:24: note: candidates are: Couple::Couple(const Person&, const Person&, const Optional<fox::Person>&)
Indeed, a conversion sequence can't contain more than one implicit user-defined conversion; the standard specifies this in C++11 12.3/4:
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
In your case, two would be required (char const[] to std::string to Person), and so implicit conversion is not possible.
You are correct that there is a problem with implicit conversion. It will only do one implicit conversion for a value, so you can do either Couple(std::string("a"), std::string("b")) or Couple(Person("a"), Person("b")), for example, but Couple("a", "b") would require the compiler to put in two implicit conversions per value. This is not allowed by the standard, because it would cause code that might be hard to understand correctly and be computationally expensive to compile.
Chained implicit conversion is not allowed. If A can implicitly convert into B and B can implicitly convert into C, then it doesn't mean that A can implicitly convert into C.
//given three objects as
A a;
B b'
C c;
//premises
b = a; //a can convert into b (implicitly)
c = b; //b can convert into c (implicitly)
//then it does not follow this
c = a; //a CANNOT convert into c (implicitly)
//you need to write this at least
c = static_cast<B>(a); //ok