c++ explicit constructor called in implicit situation - c++

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.

Related

constexpr constructor which may never be constexpr instantiated

I have come across a case of constexpr usage where I don't understand why the code compiles. Here is a minimal and reproducible example (Godbolt):
struct A
{
A(int a_)
:m_a{a_}
{
}
int m_a;
};
// Compilation fails with this structure:
struct B
{
constexpr B(A a_)
:m_a{a_}
{
}
A m_a;
};
// Compilation OK here, even if A is not constexpr.
struct BOK
{
constexpr BOK(const A& a_)
:m_a{a_}
{
}
A m_a;
};
int main()
{
// constexpr A a{2}; // Fails. OK, A's constructor is not constexpr
// constexpr B b{A(2)}; // Fails. OK, A's constructor is not constexpr
// constexpr BOK bok{A(2)}; // Fails. OK, A's constructor is not constexpr
const BOK bok{A(2)};
return 0;
}
I understand that B does not compile and the error is clear:
error: invalid type for parameter 1 of 'constexpr' function 'constexpr B::B(A)'
What I don't understand if why BOK does compile (especially since B doesn't). It seems the const& somehow makes it OK for the compiler. As I am showing in the main function, trying to instantiate a constexpr version of BOK fails to compile with error:
error: the type 'const BOK' of 'constexpr' variable 'bok' is not literal
as expected, but the struct's definition seems to be fine. It is weird because is seems the compiler thinks there might be a possibility for this to be constexpr... Is there? Why does this work?
I have tried this with both g++ and clang with the -std=c++20 switch.
If you look solely the declaration of B's constructor:
constexpr B::B(A a_);
The compiler knows that this can not possibly be constexpr, since it would have to copy the non-literal type A.
If you look at BOK's constructor:
constexpr BOK::BOK(const A& a_);
This could be called in a constant expression (Like extern A value; BOK{value}), if you don't look at the actual definition.
Until C++23, it is "ill-formed, no diagnostic required" if there is no way to call a constexpr function or constructor in a constant expression. constexpr B::B(A a_) can be ruled out fast, and that's all that GCC is willing to check (it's a trade off for slower compile times vs more correctness). Your BOK struct's constructor is also ill-formed, but the compiler just isn't warning you.
As a side note, you can make A a literal type with any constexpr constructor:
#include <bit>
struct A
{
A(int a_)
:m_a{a_}
{
}
int m_a;
constexpr A() = delete; // Now a literal type
};
struct B
{
constexpr B(A a_)
:m_a{a_}
{
}
A m_a;
};
struct BOK
{
constexpr BOK(const A& a_)
:m_a{a_}
{
}
A m_a;
};
constexpr A a{std::bit_cast<A>(2)};
constexpr B b{std::bit_cast<A>(2)};
constexpr BOK bok{std::bit_cast<A>(2)};

User-defined conversion-operator in nested class

Why does the following code not compile:
struct X
{
struct B;
struct A
{
int dummy;
operator B();
};
struct B
{
int dummy;
};
};
X::A::operator B()
{
B b;
return b.dummy = dummy, b;
}
My MSVC++ 2017 compiler says:
error C2833: 'operator B' is not a recognized operator or type
The only possible reason of this error is that struct B is not defined-yet at the point when struct A is being defined. Since the code does not seem the be buggy, my conclusion is that you have found a compiler bug.
Even though B should be looked up in the scope of X as the user defined conversion operator is being defined, MSVC seems to bungle it up.
You can give it hand by fully qualifying it:
X::A::operator X::B()
{
B b;
return b.dummy = dummy, b;
}

Copy-initialisation and explicit constructor - compiler discrepancy

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);

access to private constructor by friend class

Problem: source code (see. below) is compiled MSVC , but does not compile g++.
#include <iostream>
using namespace std;
class B;
class A
{
friend class B;
private:
int i;
A(int n) : i(n) { }
public :
A(A& a) { if (&a != this) *this = a; }
int display() { return i;}
};
class B
{
public :
B() { }
A func( int j) { return A(j); }
};
int main(int argc, char **argv)
{
B b;
A a(b.func((10)));
cout << " a.i = " << a.display() << endl;
return 0;
}
Output:
GNU g++ compilation message:
g++ -c main.cpp
main.cpp: In member function 'A B::func(int)':
main.cpp:25:38: error: no matching function for call to 'A::A(A)'
A func( int j) { return A(j); }
^
main.cpp:25:38: note: candidates are:
main.cpp:17:9: note: A::A(A&)
A(A& a) { if (&a != this) \*this = a; }
^
main.cpp:17:9: note: no known conversion for argument 1 from 'A' to 'A&'
main.cpp:14:9: note: A::A(int)
A(int n) : i(n) { }
^
main.cpp:14:9: note: no known conversion for argument 1 from 'A' to 'int'
main.cpp: In function 'int main(int, char\**)':
...
Why? Class B is a friend for class A then B has access to private constructor A(int i).
Your copy constructor must take a const reference so it can bind to a temporary A:
A(const A& a) { .... }
The C++ standard does not allow binding a non-const reference to a temporary. g++ is strict about this, while MSVC has an "extension" that breaks the rule.
Besides that, your implementation of the copy constructor looks strange. You should not be using the assignment operator there. For a class like A, you should use the implicitly generated copy constructor, in other words, remove your own:
class A
{
friend class B;
private:
int i;
A(int n) : i(n) { }
public :
int display() const { return i;}
};
It is a bug of MS VC++. It shall not compile the code. The problem is that the error message of g++ is not cllear enough.
In fact the compiler tries to elide using of a copy constructor and to build the object directly in 'a' using constructor A(int n);. But that it will be possible an appropriate copy constructor shall be available. As a temporary object should be copied (if the ilision would not be used) then the copy constructor shall have const reference to object. That is instead of A(A& a); your copy constructor shall be declared as A( const A& a); If you will make the changes then g++ will compile the code.
The most simple way to define copy constructor for your example is to write
A( const A & ) = default;
However I am not sure whether your MS VC++ compiler supports this feature.

Unable To Resolve Name In Lambda When Naming Nested Class

I'm using MSVC10.
I have a class C which is nested in class B, which in turn is nested in class A. B has a member variable of type C, and A has a vector of Bs. Like so:
class A
{
class B
{
string foo_;
class C
{
string bar_;
} c_;
};
vector<B> b_;
};
Within A I have a member function which uses for_each with a lambda, to iterate over the vector<B>.
In that lambda I try to get a reference to the B and the C (separately):
void A::Run()
{
for_each(b_.begin(), b_.end(), [](std::vector<B>::value_type& that)
{
const B& b = that;
cout << b.foo_;
const B::C& c = b.c_; // 'B' : is not a class or namespace name
// const A::B::C& c = b.c_; <-- THIS COMPILES
cout << c.bar_;
});
}
The code: const B::C& c = b.c_; results in a compiler error, "'B' : is not a class or namespace name" even though the compiler had no problem accepting const B& b = that;
Is this syntax allowed by the language?
If I change it to: const A::B::C& c = b.c_; the compiler accepts it.
Here is a complete example for you to play with:
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void foo() {}
class A
{
public:
void Run();
struct B
{
std::string foo_;
struct C
{
std::string bar_;
} c_;
};
std::vector<B> b_;
};
void A::Run()
{
for_each(b_.begin(), b_.end(), [](std::vector<B>::value_type& that)
{
const B& b = that;
cout << b.foo_;
const B::C& c = b.c_; // 'B' : is not a class or namespace name
// const A::B::C& c = b.c_; <-- THIS COMPILES
cout << c.bar_;
});
}
int main()
{
A a;
a.Run();
}
It's a bug in the compiler. The code compiles fine with MSVC 2012 RC. I believe the pertinent bug is this one.
And the pertinent part of the standard is [expr.prim.lambda] 5.1.2 clause 7:
The lambda-expression’s compound-statement yields the function-body
(8.4) of the function call operator, but for purposes of name lookup
(3.4), determining the type and value of this (9.3.2) and transforming
id-expressions referring to non-static class members into class member
access expressions using (*this) (9.3.1), the compound-statement is
considered in the context of the lambda-expression.