Copy-initialisation and explicit constructor - compiler discrepancy - c++

I've discovered a discrepancy between the Microsoft Visual C++ compiler, and gcc-4.8.1 (as provided by ideone.com). Consider the following SSCCE:
struct S
{
int x;
};
class A
{
public:
int x;
A(const S& s) : x(s.x) {}
};
class B
{
int x, y;
public:
template <typename T> explicit B(const T& t) : x(t.x), y(t.y) {}
B(const A& a) : x(a.x), y(0) {}
};
int main() {
S s = {1};
B b1 = s; // Compiles OK on MSVC++;
// Fails on gcc - conversion from ‘S’ to non-scalar type ‘B’ requested
B b2(s); // Fails on both - Error: y is not a member of S in B::B<S>(const T &)
}
I understand why the line B b2(s); fails - the explicit constructor matches so it's tried; but t.y doesn't exist. Fine.
But I can't work out whether MSVC++ is correct in allowing B b1 = s;, or whether gcc is correct in rejecting it. MSVC++ is constructing a temporary from A::A(const S&), and using that to initialise b1 via B::B(const A&); I'm not sure why gcc errors.
Which compiler's right?
(As an after note, if I remove the explicit both compilers reject B b1 = s; - presumably because the templated constructor is now fair game for the implicit construction of the temporary.)
Edit: From the comments, it appears the MSVC++ also rejects the B b1 = s; line in Visual Studio 2012, so the consensus seems to be it really is an error. In which case - what is the nature of the error? What does that error message mean?

Stolen from this answer, the standard says:
12.3 Conversions [class.conv]
4 At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
You're trying to perform two in one step, S which needs to be converted for B's constructor that accepts an A, and then another for A's constructor that accepts an S. The solution is to first cast S into an A:
B b1 = static_cast<A>(s);

Related

C++ template: no matching function for call

I cannot understand why this program fails to compile both with g++ 7.3 or clang++ 5.0 using -std=c++14.
A can be initialized from a const int as shown. A const reference to A can also be create from a const int but call to f(const A &) with a const int fails. Why?
#include <iostream>
struct V {
int i;
template <class T>
V(const T &t) : i{t} {}
};
struct A {
int i;
A(V v) : i{v.i} {}
};
void f(const A &) {}
int main() {
const auto i = 42;
const A a1{i}; // OK
std::cout << a1.i << '\n'; // 42
const A &a2 = A{i}; // OK
std::cout << a2.i << '\n'; // 42
f(i); // no matching function for call to 'f'
return 0;
}
Given f(i);, copy initialization is applied. And i (with type const int) needs to be converted to A, two user-defined conversions are required; from const int to V, and from V to A. But only one user-defined conversion is allowed in one implicit conversion sequence.
Bot const A a1{i}; and const A &a2 = A{i}; are direct initialization, only one implicit conversion from i (with type const int) to the argument of A's constructor (i.e. V) is required, so they work fine.
Note the difference between copy initialization and direct initialization,
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.
As a workaround, you can perform explicit conversion on i before passing it to f().
Converting i to an A for the purpose of the function call will require two user defined conversions (int -> V -> A). The standard places a hard limit of a single user defined conversion on each implicit conversion sequence.
The same would happen if you were to try and bind a2 to i "directly". So you need to do a functional style cast (A{i}) when giving an argument to f as well.
You need two consecutive implicit type conversion here however C++ can do single conversion implicitley for you. If you want to let the compiler to generate the correct typed code for you, use template for the function f like following.
template <typename T>
void f(const T & x) { std::cout << x << std::endl;}
The reason why you need two type conversion is because of having only one constructor which takes type V in the struct. If you want to get rid of two type conversion as a second solution, you can add another constructor which takes int as a parameter like following
struct A {
int i;
A(V v) : i{v.i} {}
A(int theI) : i{theI} { }
};
Two user defined conversions are not supported in copy initialization. Simple work around to the problem is to wrap i with A while passing to funtion f with f(A(i))

clang: initialising a lock reference from a mutex

This program is compiled by clang:
#include <mutex>
int main() {
std::mutex mtx;
const std::lock_guard<std::mutex>& lock(mtx);
return 0;
}
Other major compilers refuse it (I have tried gcc, msvc, and icc). This is an error message from gcc:
error: invalid initialization of reference of type ‘const
std::lock_guard<std::mutex>&’ from expression of type ‘std::mutex’
Others give similar errors.
Is clang right or wrong? Can this be reproduced with a simpler example not involving library classes? I have tried but to no avail.
Edit this seems to be the minimal reproduction:
struct A {};
struct X
{
explicit X(A&) {};
};
int main()
{
A a;
const X& x(a);
}
Interestingly, an int in place of A does trigger the error message in clang (which is why I could not reproduce this initially).
I don't have the relevant chapter and verse of the C++ standard; I can only refer to CppReference on Converting Constructors right now (emphasis mine):
A constructor that is not declared with the specifier explicit and which can be called with a single parameter (until C++11) is called a converting constructor.
Unlike explicit constructors, which are only considered during direct initialization (which includes explicit conversions such as static_cast), converting constructors are also considered during copy initialization, as part of user-defined conversion sequence.
So:
struct A {};
struct X
{
explicit X(A const &) {};
};
int main()
{
A a;
const X& x1(A()); // OK, direct init (no A object after init)
const X& x3(a); // NOK, copy init
}

c++ explicit constructor called in implicit situation

I compiled the code below using g++ 6.3.0, with -std=c++14 option.
#include <utility>
#include <iostream>
struct A{
int x;
A(const A&)=default;
A(int x):x(x){}
};
struct B{
A a;
template<class... Args>
B(Args&&... args):a(std::forward<Args>(args)...){
std::cout<<"1!"<<std::endl;
}
explicit B(const A& a):a(a){std::cout<<"2!"<<std::endl;}
};
struct C:B{
using B::B;
};
int main(){
A a{2};
const A& aref=a;
C c=aref; //Implicit conversion
}
I expected it to output "1!" since the conversion is implicit, but it output "2!". If I comment out the template constructor, it will not compile. Is this the correct behavior, or is this some kind of g++ bug?
Yes, your program shall print 1!.
A simplified version of the program showing compiler divergence is
struct B {
int b;
constexpr B(auto) { b = 1; }
constexpr explicit B(int) { b = 2; }
};
struct C : B {
using B::B;
};
constexpr B b = 0;
static_assert( b.b == 1 ); //ok everywhere
constexpr C c = 0;
static_assert( c.b == 1 ); //ok in Clang only
Demo: https://gcc.godbolt.org/z/hva4f5qs5
As quoted in related GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85251 : [namespace.udecl] p13 does specify what should happen here:
Constructors that are named by a using-declaration are treated as though they
were constructors of the derived class when looking up the constructors of the
derived class ([class.qual]) or forming a set of overload candidates
([over.match.ctor], [over.match.copy], [over.match.list]).
So explicit constructor from B shall remain explicit in C after using B::B;, and only Clang correctly process the above program at this moment.
And even easier way to see that other compilers are wrong, is to remove B(auto) constructor:
struct B {
int b;
constexpr explicit B(int) { b = 2; }
};
struct C : B {
using B::B;
};
constexpr C c = 0; // error everywhere
Now all compilers correctly reject the code (demo: https://gcc.godbolt.org/z/P3zqxMvvn) even though they did not call the removed constructor in the previous example.

Error: no matching function for call to 'A::A(A)'

I have coded a very basic class
class A
{
int n;
public:
A(int& val){val=n;}
A(const int& val=0){n=val;}
A(A& val){n=val.n;}//work without this constructor
};
int main()
{
A a=3;//want to call A::A(const int&)
return 0;
}
I don't want to create a constructor with a copy from an instance of A (for a future use)
What's wrong with this simple code?
Error message :
...\main.cpp||In function 'int main()':|
...\main.cpp|16|error: no matching function for call to 'A::A(A)'|
...\main.cpp|16|note: candidates are:|
...\main.cpp|11|note: A::A(A&)|
...\main.cpp|11|note: no known conversion for argument 1 from 'A' to 'A&'|
...\main.cpp|10|note: A::A(const int&)|
...\main.cpp|10|note: no known conversion for argument 1 from 'A' to 'const int&'|
...\main.cpp|9|note: A::A(int&)|
...\main.cpp|9|note: no known conversion for argument 1 from 'A' to 'int&'|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
It seems 3 is considered as an instance of A?
If I add A(const A& val){n=val.n;}
the constructor A(const int&) is chosen.
What can I do to compile successfully without A(const A& val)?
The issue is your copy constructor: A(A& val) { n=val.n; }.
Given this line A a = 3; one possibility is to use A a = A(3) which in turn will use the copy constructor. However, it is against the standard to bind a temporary to non-const. A proper copy constructor will solve the problem.
Example Code
#include <iostream>
class A
{
int n;
public:
A(int& val) : n(val) { std::cout << "A(int&)\n"; }
A(const int& val=0) : n(val) { std::cout << "A(const int&)\n"; }
A(const A& val) : n(val.n) { std::cout << "A(const A&)\n"; }
};
int main()
{
A a = 3;
return 0;
}
Example Output
A(const int&)
Live Example
Note:
The above code makes proper use of the initialization lists
The output shows the copy constructor is not actually invoked
The line of code;
A a = 3;
Is using copy initialisation to work. For that to work, a copy of A is needed and is duly made. You have two constructors taking an int as an argument (and neither are explicit).
The copy cannot bind to A(A& val) because normal references don't bind to temporary values.
Possible solutions, direct initialisation;
A a { 3 };
Add or change the copy constructor to a move constructor;
A(A&& val);
Add a const to the copy constructor (although this is not what you wanted, but it does work);
A(A const& val);
Note both clang and g++ reject the original code correctly, but VC++ accepts it.
In this statement
A a=3;//want to call A::A(const int&)
there is created a temporary object of type A using constructor
A(const int& val=0){n=val;}
However a temporary object can be bound to a constant reference. So the compiler need that the copy constructor
A( const A& val){n=val.n;}//work without this constructor
^^^^^
would be at least accessible (even if it will not be called due to the copy constructor elision).
But the class does not has this constructor and the compiler issues an error.
On the other hand when you comment this constructor then the compiler itself implicitly defines this constructor and the code is compiled.
Take into account that this constructor
A(int& val){val=n;}
^^^^^^
does not make sense.:)

default copy assignment operator doesn't pass the is_copy_assignable test

#include <type_traits>
struct A {};
struct B {
B(A& a) : a(a) {}
//B& operator=(B const& b) = default; // LINE 1
//B& operator=(B const& b) { a = b.a; return *this; } // LINE 2
A& a;
};
static_assert(std::is_copy_constructible<B>::value, ""); // pass
static_assert(std::is_copy_assignable<B>::value, ""); // fail
int main() {
A a;
B b(a);
return 0;
}
Compiler: g++ 5.1
The default copy constructor is fine, but the default copy assignment operator fails (even with LINE 1 uncommented).
Declaring it explicitly (uncommenting LINE 2) works though. Is it a bug?
No, it's not a bug. Your class doesn't have a defined copy assignment operator, because you have a reference member. If you have a class with reference semantics, it is not obvious whether you would want assignment to just assign from one reference to another (which results in copy assigning the referenced object), or whether the reference should be rebound (something you can't actually do, although this is usually what you would want). So the standard simply doesn't generate a default assignment operator, but allows you to define one manually with the semantics you want.