This question already has answers here:
Do I really need to implement user-provided constructor for const objects?
(4 answers)
Closed 8 years ago.
Consider following example:
#include <iostream>
#include <type_traits>
struct A
{
//A() = default; // does neither compile with, nor without this line
//A(){}; // does compile with this line
int someVal{ 123 };
void foobar( int )
{
};
};
int main()
{
const A a;
std::cout << "isPOD = " << std::is_pod<A>::value << std::endl;
std::cout << "a.someVal = " <<a.someVal << std::endl;
}
See Live example
This does compile with g++ but does not compile with clang++, tried with following command: clang++ -std=c++11 -O0 main.cpp && ./a.out
Compile error from clang:
main.cpp:19:13: error: default initialization of an object of const type 'const A' requires a user-provided default constructor
I learned from This Stack Overflow Question, that non-POD classes get default constructor. This is even not necessary here because the variable has c++11-style default initialization
Why does this not for clang?
Why does A() = default; not work, too?
This is addressed in CWG issue #253 which discusses the need for a user provided constructor for empty objects or objects whose subobjects are fully initialized (which is the case in your example).
Quoting part of the linked issue
Notes from the August, 2011 meeting:
If the implicit default constructor initializes all subobjects, no initializer should be required.
Technically it is an active issue but given that note it seems likely that it'll be resolved the way gcc chose to implement it.
Clang, on the other hand, has chosen to wait until the issue is resolved before implementing a solution.
In Clang, we're waiting for the issue to actually be resolved before we take a direction on it.
So, as it currently stands, clang is correct.
You quoted the answer yourself. In the SO answer that you linked, there is the following quote from the standard (section 6.8.6precisely):
If a program calls for the default initialization of an object of a
const-qualified type T, T shall be a class type with a user-provided
default constructor.
emphasis mine. The line
A() = default;
obviously does not provide a constructor, it does the opposite by telling the compiler that you don't want to provide one, thus your code doesn't compile. However, once you provide the constructor by uncommenting
A(){};
it works fine. So, to summarize, the error that clang shows is per standard, and the behaviour of gcc is probably a bug.
Related
The following code compiles using gcc 11+ but fails to compile with gcc version <= 10.
#include <stdint.h>
typedef volatile struct s {
uint32_t receiver:1;
uint32_t transmitter:1;
uint32_t _res:30;
} s_t;
s_t get_struct(void){
struct s m = {0};
return m;
}
int main() {
// compiles using gcc 11
// does not compile using gcc 10 and lower
s_t io1 = get_struct();
return 0;
}
You can try for yourself here:
https://godbolt.org/z/cavYxKdKo
Can you please explain why this is?
FYI, the code compiles with earlier gcc version if the individual struct members are qualified with volatile (instead of the struct itself). I don't know why this is as I thought the semantics are the same (at least they are in C).
typedef struct s {
volatile uint32_t receiver:1;
volatile uint32_t transmitter:1;
volatile uint32_t _res:30;
} s_t;
Similar questions:
volatile struct = struct not possible, why?
Copy constructor for C volatile bitfield struct
Thanks to helpful comments to my question, I try to answer the question myself with the best of my knowledge. Please comment if you spot mistakes.
Whether the code compiles or not has to do with the C++ version used.
The given code
compiles with C++17 (use gcc 11 or gcc 10 with option -std=c++17) and
does not compile with C++14 (use gcc 10 or gcc 11 with option -std=c++14).
The difference between both C++ versions with regard to this issue is based on "mandatory elision of copy/move operations" starting with C++17, see here.
Using C++14, appropriate constructors need to be defined in order to construct s_t io1.
To see this more easily, in the linked example switch to "x64-64 clang 13.0.0)". This compiler provides diagnostic messages that are easier to comprehend in my opinion. "clang" compiles the code when given the option -std=c++17. Otherwise, it emits error messages:
error: no matching constructor for initialization of 's_t' (aka 'volatile s')
It also lists several implicitly defined constructors that were rejected as "not viable".
In other words, we need to provide the proper constructor manually (aka. user-defined constructor).
First, we need to take care of the initialization of struct s m = {0}; because as soon as we add any user-define constructors, there won't be an implicitly defined constructor any more. See default constructor. Further, aggregate initialization does not apply any more as soon as there are user-defined constructors added to the struct.
This will do s(int i) {} for compiling (let's omit initialization or other code from constructors).
Second, we will need to provide a constructor for argument type s_t.
If I declare a constructor s(volatile struct s l) {}
"clang" complains with
error: copy constructor must pass its first argument by reference
s(volatile struct s l) { }
If I declare a copy constructor s(volatile struct s & l) { }
"clang" complains with
error: no matching constructor for initialization of 's_t' (aka 'volatile s')
However, if I declare a move constructor s(volatile struct s && l) { }
the code compiles (with clang and gcc, C++14 and C++17).
This is the resulting code to try for yourself.
I compile this code below with GCC 11.1.0 with a flag -std=c++17. It occurs that on the stdout is printed initializer_list.
I compiled the same code with MSVC with the flag -std=c++17 but it printed "copy constructor". Which compiler is more compliant with the cpp standard? Is compiler free to choose one of the constructors?
#include <iostream>
using namespace std;
struct S
{
S(int) { }
S(initializer_list<int>) { cout << "initializer_list"; }
S(const S&) { cout << "copy constructor"; }
operator int() const { return 1; };
};
int main()
{
S s1(20);
S s2{ s1 };
}
The compiler is pretty much never "free to choose" for stuff like this. If it were, we wouldn't be able to write pretty much any portable C++ code.
[over.match.list] does give priority to initializer_list constructors. Constructor function overloading under the rules of list initialization gets invoked at step 3.6. Steps 3.1-3.5 do not apply, as your type doesn't qualify for any of those cases. Step 3.1 is particularly interesting, as it is specifically meant to invoke copy constructors instead of doing other things, but it also only applies to aggregates. Which your type is not.
Since your type is implicitly convertible to int, and your type takes an initializer_list<int>, there is a valid way to build an initializer_list that matches a constructor for the type in question. Therefore, this is the constructor [over.match.list] will select.
So in this case, VC++ is wrong. As is Clang, apparently.
This question already has answers here:
Program being compiled differently in 3 major C++ compilers. Which one is right?
(2 answers)
Closed 7 years ago.
I saw this snippet on Meeting C++ (#meetingcpp)
Following code compiles fine on clang and MSVC (Can try here) but fails on gcc and icc.
#include <iostream>
using namespace std;
struct B {};
struct C {
C() { cout << "C()\n"; }
C(B *) { cout << "C(B *)\n"; }
};
B *p = nullptr;
int main() {
C::C(p);
return 0;
}
Is this a known bug in Clang and MSVC or there are any chances this code may be legal?
Type of p is B *, but C::C should not compile?
This is a known bug in Clang, bug reports 23253, 23254 and 13403 are all reports of the issue. Ironically, this question is actually a duplicate of Program being compiled differently in 3 major C++ compilers. Which one is right?.
According to the standard 12.1/p2 Constructors [class.ctor] (Emphasis Mine):
A constructor is used to initialize objects of its class type.
Because constructors do not have names, they are never found during name lookup; however an explicit type conversion using the
functional notation (5.2.3) will cause a constructor to be called to
initialize an object. [ Note: For initialization of objects of class
type see 12.6. — end note ]
Thus, you can't call a constructor directly, because constructors do not have names and they are never found during name lookup.
Consequently, GCC is conforming while CLANG and VC++ are not.
As an interesting follow-up (not of big practical importance though) to my previous question:
Why does C++ allow us to surround the variable name in parentheses when declaring a variable?
I found out that combining the declaration in parentheses with injected class name feature may lead to surprising results regarding compiler behavior.
Take a look at the following program:
#include <iostream>
struct B
{
};
struct C
{
C (){ std::cout << "C" << '\n'; }
C (B *) { std::cout << "C (B *)" << '\n';}
};
B *y = nullptr;
int main()
{
C::C (y);
}
Compiling with g++ 4.9.2 gives me the following compilation error:
main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
It compiles successfully with MSVC2013/2015 and prints C (B *)
It compiles successfully with clang 3.5 and prints C
So obligatory question is which one is right? :)
(I strongly swayed towards clang version though and msvc way to stop declaring variable after just changing type with technically its typedef seems kind of weird)
GCC is correct, at least according to C++11 lookup rules. 3.4.3.1 [class.qual]/2 specifies that, if the nested name specifier is the same as the class name, it refers to the constructor not the injected class name. It gives examples:
B::A ba; // object of type A
A::A a; // error, A::A is not a type name
struct A::A a2; // object of type A
It looks like MSVC misinterprets it as function-style cast expression creating a temporary C with y as a constructor parameter; and Clang misinterprets it as a declaration of a variable called y of type C.
G++ is correct as it gives an error. Because the constructor could not be called directly in such a format without new operator. And although your code calls C::C, it looks like an constructor call. However, according to the C++11 standard 3.4.3.1, this is not a legal function call, or a type name (see Mike Seymour's answer).
Clang is wrong since it even does not call the correct function.
MSVC is something reasonable, but still it does not follow the standard.
An hour ago I posted an answer here which according to me was correct. However my answer was downvoted by Martin B. He said
You're just lucky and are getting zeros because the memory that i was placed in happened to be zero-initialized. This is not guaranteed by the standard.
However after reading Michael Burr's answer here and trying the following sample code
1)
#include <cassert>
struct B { ~B(); int m; };
int main()
{
B * b = new B();
assert(b->m == 0);
}
I got a debug error on MSVC++ 2010.
I got a similar error when I tried the following code [My answer here] on MSVC++2010
2)
#include <cassert>
struct Struct {
std::string String;
int Int;
bool k;
// add add add
};
struct InStruct : Struct
{
InStruct() : Struct() {}
};
int main()
{
InStruct i;
assert(i.k == 0);
}
Neither (1) nor (2) gave any such error on gcc/Clang which made me think if MSVC++2010 does not support C++03. I am not sure.
According to Michael Burr's post [in C++03]
new B() - value-initializes B which zero-initializes all fields since its default ctor is compiler generated as opposed to user-defined.
The Standard says
To value-initialize an object of type Tmeans:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if Thas no accessible default constructor);
.....
otherwise, the object is zero-initialized
From the first point if there is no user declared default constructor the compiler synthesized default constructor will be called which will zero initialize all the fields (according to last point).
So where am I wrong? Is my interpretation of value initialization correct?
Visual Studio has known bugs in all current versions (2005, 2008, 2010) where it doesn't correctly implement value-initialization for non-POD types that don't have a user declared constructor.
By the language rules none of you asserts should fire but do exhibit the compiler issues. These are some of the bug reports, note that they are all closed or resolved as "Won't Fix".
http://connect.microsoft.com/VisualStudio/feedback/details/564268/c-value-initialization
http://connect.microsoft.com/VisualStudio/feedback/details/484295/vc-does-not-value-initialize-members-of-derived-classes-without-user-declared-constructor
http://connect.microsoft.com/VisualStudio/feedback/details/100744/value-initialization-in-new-expression
For people who stumble upon this question in 2015, like me:
All of the above issues have been fixed in VS 2015. Value initialization now works as defined in the standard.