I have the following code snippet:
class A
{
public:
A() : x_(0), y_(0) {}
A(int x, int y) : x_(x), y_(y) {}
template<class T>
A(const T &rhs) : x_(rhs.x_), y_(rhs.y_)
{
}
int x_, y_;
};
class B
{
public:
B() {}
operator A() const { return A(c[0],c[1]); }
int c[2];
};
void f()
{
B b;
(A)b; // << here the error appears, compiler tries to use
// template<class T> A(const T &rhs)
}
Why compiler uses A's constructor? How can I make it use B's conversion operator to A?
I use MSVS2010 compiler. It gives me these errors:
main.cpp(9): error C2039: 'x_' : is not a member of 'B'
main.cpp(17) : see declaration of 'B'
main.cpp(28) : see reference to function template instantiation 'A::A<B>(const T &)' being compiled
with
[
T=B
]
main.cpp(9): error C2039: 'y_' : is not a member of 'B'
main.cpp(17) : see declaration of 'B'
UPD:
All right, implicit convert as Nawaz said really works. Let's make it more complicated, how make the following code work?
void f()
{
std::vector<B> b_vector(4);
std::vector<A> a_vector( b_vector.begin(), b_vector.end() );
}
UPD: A is the class in 3rd party lib which code I can't edit, so I can't remove A's converting constructor.
UPD: the simplest solution I've found for the moment is to define specialization of converting constructor for B. It can be done outside of 3rd party lib:
template<> A::A( const B &rhs ) : x_(rhs.c[0]), y_(rhs.c[1]) {}
The reason is not only because it thinks (A)b is same as A(b). The standard says this about explicit type-conversion (5.4):
The conversions performed by
a const_cast (5.2.11),
a static_cast (5.2.9),
a static_cast followed by a const_cast,
a reinterpret_cast (5.2.10), or
a reinterpret_cast followed by a const_cast,
can be performed using the cast
notation of explicit type conversion.
The same semantic restrictions and
behaviors apply.
Essentially this means that even for explicit type-conversion of (A)b (i.e. if you used ((A)b); to prevent from it being a variable declaration). it'd use the rules of static_cast. Now let's take a look at what the standard says about static_cast (5.2.9):
An expression e can be explicitly
converted to a type T using a
static_cast of the form
static_cast(e) if the declaration
“T t(e);” is well-formed, for some
invented temporary variable t (8.5).
The effect of such an explicit
conversion is the same as performing
the declaration and initialization and
then using the temporary variable as
the result of the conversion. The
result is an lvalue if T is a
reference type (8.3.2), and an rvalue
otherwise. The expression e is used as
an lvalue if and only if the
initialization uses it as an lvalue.
If you do static_cast<A>(b), it basically sees if A(b) is well-formed; and it is. Just because the actual instantiation of the template function copy-constructor fails, it doesn't make the actual declaration is ill-formed, hence it uses it and ultimately fails.
From 5.4/1 and 5.4/5 the C-cast picks the "best choice" C++ cast from a list. In this case, that's a static_cast.
Then from 5.2.9/2:
An expression e can be explicitly
converted to a type T using a
static_cast of the form
static_cast(e) if the declaration
“T t(e);” is well formed, for some
invented temporary variable t (8.5).
The effect of such an explicit
conversion is the same as performing
the declaration and initialization and
then using the temporary variable as
the result of the conversion. The
result is an lvalue if T is a
reference type (8.3.2), and an rvalue
otherwise. The expression e is used as
an lvalue if and only if the
initialization uses it as an lvalue.
So it picks the constructor before even attempting any other option.
In this case you've defined two conversions to get to the same end result, but the language has specific rules dictating that it will always use the available constructor. You should probably leave the constructor and change the operator to an explicit as-type function instead.
EDIT for OP's edit:
I don't think you'll be able to use the vector iter, iter constructor. You'll need to start with an empty vector and either use a for loop with push_back or use std::transform.
(A)b; // << here the error appears, compiler tries to use
This is explicit casting to A. Hence the constructor of A gets invoked to convert b to A.
A a(1,2);
a = b ; //this will invoke user-defined conversion of B (implicit conversion)
Demo : http://www.ideone.com/K9IxT
As mentioned, given the choice between a well-formed constructor and a conversion operator, the compiler will always call the constructor. But you could call the conversion operator directly, if you wish:
A a = b.operator A();
As for the second part of your question on how to make it working, I would use std::transform instead of modifying/adding something in the 3rd-party library namespace, unless only this library is well documented and it is intended that you should specialize this constructor:
a_vector.reserve( b_vector.size() );
std::transform( b_vector.begin(), b_vector.end(),
std::back_inserter( a_vector ), boost::bind( &B::operator A, _1 ) );
The syntax (A)b is the same as A(b), which is a construction. If you want a cast, then you must explicitly use static_cast, for example.
Related
I was trying to overload the casting operator in C++ for practice, but I encountered a problem and I can't figure out the issue. In the example, you can implicitly cast fine, but it causes an error when you try to explicitly cast.
struct B
{
B() = default;
B( B& rhs ) = default;
};
struct A
{
operator B()
{
return B();
}
};
int main()
{
A a;
B example = a; //fine
B example2 = static_cast<B>(a); //error
}
The error is:
error C2440: 'static_cast': cannot convert from 'A' to 'B'
message : No constructor could take the source type, or constructor overload resolution was ambiguous
The problem only appears if you define the copy constructor in the B structure. The problem goes away, though, if you define the move constructor, too, or make the copy constructor take in a const B& ( B( const B& rhs ) ).
I think the problem is that the explicit cast is ambiguous, but I don't see how.
I was looking at a similar problem, here, but in that case I could easily see how the multiple options for casting led to ambiguity while I can't here.
static_cast<B>(a);
This expression is an rvalue, more loosely described as a temporary value.
The B class has no suitable constructor. The B( B &rhs) constructor is not suitable, mutable lvalue references don't bind to temporaries, hence the compilation failure.
Before C++17:
Both lines do effectively the same thing. B example = /*...*/; is copy-initialization which will first convert the right-hand side to the type B if necessary in some suitable manner, resulting in a temporary object from which example is then initialized by choosing a suitable constructor (typically a copy or move constructor).
Because A is not related to B via inheritance, there is no way to bind a directly to the rhs parameter in the (copy) constructor of B. There must first be a conversion from a to a B which is possible implicitly or explicitly via the conversion function you defined.
The result of the conversion will always be a prvalue. A prvalue however can not be bound to a non-const lvalue reference. Therefore it doesn't help that the conversion is possible, the B( B& rhs ) constructor overload is still not viable.
A constructor B( const B& rhs ) (which is also the signature of the implicitly-declared copy constructor if you don't declare one yourself) would however be viable because the prvalue resulting from the conversion can be bound to a const lvalue reference.
Therefore both lines are ill-formed, before C++17.
Since C++17 the mandatory copy elision rules state that initialization of a variable of a type B from a prvalue of type B is done exactly as if the variable was directly initialized by the initializer of the prvalue, so that no overload resolution on the constructor will be performed at all. Whether there is a copy constructor or how exactly it is declared is then irrelevant. Similarly the rules for copy-initialization from a non-reference compatible lvalue such as a have been updated to have the same effect.
So since C++17 both lines will compile.
If you are using MSVC in a default configuration with the language standard set below C++17, the first line will compile, but that is not standard-conforming. By default when set to language versions below C++20 MSVC uses a slight dialect of C++ which is not conforming to the standard in some areas. You can set the standards conformance mode (/permissive- flag) to set MSVC to behave (more) standard-conforming.
Consider the following two classes:
class B
{
public:
B() { }
B(const B& b) = delete; //Move ctor not implicitly declared
};
class A
{
public:
A() { }
operator B()
{
return B();
}
};
I can see why this code compiles fine:
A a;
B b = a;
Following the rules of copy-initialization, the object "a" gets converted to a prvalue of type B and since in C++17 the copy constructor is not needed anymore there's no error:
If T is a class type, and the cv-unqualified version of the type of
other is not T or derived from T, or if T is non-class type, but the
type of other is a class type, user-defined conversion sequences that
can convert from the type of other to T (or to a type derived from T
if T is a class type and a conversion function is available) are
examined and the best one is selected through overload resolution. The
result of the conversion, which is a prvalue temporary (until
C++17)prvalue expression (since C++17) if a converting constructor was
used, is then used to direct-initialize the object. The last step is
usually optimized out and the result of the conversion is constructed
directly in the memory allocated for the target object, but the
appropriate constructor (move or copy) is required to be accessible
even though it's not used. (until C++17)
However why does this direct list-initialization compile too?
A a;
B b{ a };
I couldn't find any wording in the list-initialization stating the compiler should attempt to convert A into B in this case. Only that overload resolution on constructors is considered:
If the previous stage does not produce a match, all constructors of T
participate in overload resolution against the set of arguments that
consists of the elements of the braced-init-list, with the restriction
that only non-narrowing conversions are allowed
However in this case the copy constructor is deleted, so shouldn't it not be selected by overload resolution?
This is CWG 2327. You're correct as far as the standard goes, but some compilers additionally consider conversion functions in this context as well - because it really makes sense to.
I've recently read somewhere (can't remember where) about using braces to allow multiple user-defined conversions, but there seems to be a difference between conversion by constructor and conversion by conversion method that I don't understand.
Consider:
#include <string>
using ::std::string;
struct C {
C() {}
};
struct A {
A(const string& s) {} // Make std::string convertible to A.
operator C() const { return C(); } // Makes A convertible to C.
};
struct B {
B() {}
B(const A& a) {} // Makes A convertible to B.
};
int main() {
B b;
C c;
// This works.
// Conversion chain (all thru ctors): char* -> string -> A -> B
b = {{"char *"}};
// These two attempts to make the final conversion through A's
// conversion method yield compiler errors.
c = {{"char *"}};
c = {{{"char *"}}};
// On the other hand, this does work (not surprisingly).
c = A{"char *"};
}
Now, I may be misinterpreting what the compiler is doing, but (based on the above and additional experimentation) it seems to me that it's not considering conversions by conversion-method. Reading through Sections 4 and 13.3.3.1 of the standard, however, I wasn't able to find a clue why this is. What is the explanation?
Update
Here's another interesting phenomenon I'd like explained. If I add
struct D {
void operator<<(const B& b) {}
};
and in main:
D d;
d << {{ "char *" }};
I get an error, but if instead I write d.operator<<({{ "char *" }}); it works fine.
Update 2
Looks like Section 8.5.4 in the standard may hold some answers. I'll report my findings.
There is one user conversion possible.
In b = {{"char *"}};
we actually do
b = B{{"char*"}}; // B has constructor with A (and a copy constructor not viable here)
so
b = B{A{"char*"}}; // One implicit conversion const char* -> std::string
in c = {{"const char*"}}, we try
c = C{{"char *"}}; // but nothing to construct here.
Digging through Section 8.5.4 of the standard and following various cross references therein, I think I understand what's going on. Of course, IANAL, so I might be wrong; this is my best effort.
Update: The previous version of the answer actually used multiple conversions. I've updated it to reflect my current understanding.
The key to unraveling the mess is the fact that a braced-init-list is not an expression (which also explains why d << {{"char *"}} won't compile). What it is is special syntax, governed by special rules, that is allowed in a number of specific contexts. Of these contexts, the relevant ones for our discussion are: rhs of assignment, argument in a function call, and argument in a constructor invocation.
So what happens when the compiler sees b = {{"char *"}}? This is a case of rhs of assignment. The applicable rule is:
A braced-init-list may appear on the right-hand side of ... an assignment defined by a user-defined assignment operator, in which case the initializer list is passed
as the argument to the operator function.
(Presumably, the default copy assignment operator is considered a user-defined assignment operator. I couldn't find a definition of that term anywhere, and there doesn't seem to be any language allowing the brace syntax specifically for default copy assignment.)
So we are reduced to argument passing to the default copy assignment operator B::operator=(const B&), where the argument being passed is {{"char *"}}. Because a braced-init-list is not an expression, there is no issue of conversion here, but rather a form of initialization of a temporary of type B, specifically, so called list initialization.
If no viable initializer-list constructor is found, overload resolution is performed again, where the
candidate functions are all the constructors of the class T and the argument list consists of the elements
of the initializer list.
So the compiler strips off the outer pair of braces and performs overload resolution using {"char *"} as the argument. This succeeds, matching the constructor B::B(const A&) because there is again list initialization of a temporary of type A in which overload resolution succeeds to match A::A(const string&) for the argument "char *", which is possible through the one alloted user-defined conversion, namely, from char* to string.
Now, in the case of c = {{"char *"}} the process is similar, but when we try to list-initialize a temporary of type C with {{"char *"}}, overload resolution fails to find a constructor that matches. The point is that by definition list-initialization only works through a constructor whose parameter list can be made to match the contents of the list.
struct Foo
{
explicit Foo(int a):m(a){}
int padd1, m, padd2;
};
void Bar(Foo){}
int main()
{
Bar(11); // OK, gives error
auto x = static_cast<Foo>(37);
x.m;
}
Is it ok, that static_cast construct Foo object even though its constructor is marked explicit?
It works in MSVC2013 and GCC http://ideone.com/dMS5kB
Yes, static_cast will use the explicit constructor.
5.2.9 Static cast [expr.static.cast]
4 An expression e can be explicitly converted to a type T using a
static_cast of the form static_cast<T>(e) if the declaration T t(e);
is well-formed, for some invented temporary variable t (8.5). The
effect of such an explicit conversion is the same as performing the
declaration and initialization and then using the temporary variable
as the result of the conversion. The expression e is used as a glvalue
if and only if the initialization uses it as a glvalue.
explicit combined with constructor means that compiler won't perform any implicit conversion from int to Foo , asking one to deliberately cast it.
If your ctor wasn't explicit , even this expression Bar('a'); would compile.
In the example:
struct A{ };
struct B
{
B(){ };
operator A(){ return A(); }
};
const B b;
void foo(A){ };
int main(){ foo(b); } //error: no matching function for call to 'foo'
DEMO
compiler throws an error. But what the Standard says is (section 13.3.3.1.2/1 [over.ics.user]):
A user-defined conversion sequence consists of an initial standard
conversion sequence followed by a userdefined conversion (12.3)
followed by a second standard conversion sequence. [...]
If the user-defined conversion is specified by a conversion function
(12.3.2), the initial standard conversion sequence converts the source
type to the implicit object parameter of the conversion function.
Therefore in the example the first standar conversion should conver const B to B and then call the conversion function. But it didn't, why? How does it exactly work? Note the example will work fine if we replace operator A(){ return A(); } with operator A() const{ return A(); }
You do not demand a conversion sequence here, like the one you picked from the standard.
You are invoking
B::operator A()
on an object declared as "const B".
Actually, 3.9.1 says:
A non-static member function may be declared const, volatile, or const
volatile. These cv-qualifiers affect the type of the this pointer
(9.3.2). They also affect the function type (8.3.5) of the member
function; a member function declared const is a const member function
Moreover 9.3.2/3:
A cv-qualified member function can be called on an object-expression
(5.2.5) only if the object-expression is as cv-qualified or
less-cv-qualified than the member function.
As with any other method invocation, you are running into non-const method invocation on a const object. That protection is not broken for obvious reasons.
BTW, it seems that you answered the question yourself. Otherwise, hope this helps.