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.
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.
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.
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.
Based on this
Incidentally, you can do this kind of assignment of a pointer to a
function as long as the target’s exception specification is no more
restrictive than the source’s
class A{};
class B{};
class C{};
void f() throw(A,B,C) {}
void (*pf)() throw(A,B);
int main()
{
pf = f; // pf is more restrictive than that of f. I expect an error here!
}
The last statement should not pass the compiler. However, I have tried VS2010 and GCC latest version. Neither or them complain about it.
Question> Why?
This looks like a bug in GCC and VS2010. Clang won't compile it, and it should not compile according to [except.spec]/5:
... A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization.
The standard also includes the following example:
class A { /*...*/ };
void (*pf1)(); // no exception specification
void (*pf2)() throw(A);
void f() {
pf1 = pf2; // OK: pf1 is less restrictive
pf2 = pf1; // error: pf2 is more restrictive
}
It's a long-standing bug in g++: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12255
The underlying issue is that G++ just ignores exception-specifications on
non-function declarations. I think this choice originated in the uncertainty
about whether or not the exeception-specification would become part of a
function type rather than remain associated with the declaration.
It wouldn't surprise me if VS was similar in this regard.
Recently I ran into a compiler error in a legacy environment using Borland C++ 5.2. I had a .cpp file which included a header from some C source which I don't control. The header contained a struct definition which included const members, and the compiler complained about a "constant member in class without constructors". On investigation, this error seems to be compiler-dependent. Here's some sample code w/ results from various compilers:
#include <stdio.h>
typedef struct {
const float a;
} _floater;
int main()
{
_floater f = {5.1F};
printf("%f\r\n",f.a);
return 0;
}
Borland 5.2
E:\Projects\Scratchpad>bcc32 -P const_float.c
Borland C++ 5.2 for Win32 Copyright (c) 1993, 1997 Borland International
const_float.c:
Error const_float.c 13: Constant member ' ::a' in class without constructors
*** 1 errors in Compile ***
Microsoft VS 2003 .NET:
E:\Projects\Scratchpad>cl /TP const_float.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
const_float.c
const_float.c(19) : error C2552: 'f' : non-aggregates cannot be initialized with
initializer list
Microsoft VS 2008:
C:\Projects\Scratchpad>cl /TP const_float.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
const_float.c
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation. All rights reserved.
/out:const_float.exe
const_float.obj
C:\Projects\Scratchpad>const_float.exe
5.100000
G++ 3.3.3
$ g++ const_float.c -o const_float.exe
const_float.c:25:2: warning: no newline at end of file
$ ./const_float.exe
5.100000
Note that Borland fails at the declaration of the struct, because it has a const member but no constructors, while VS 2003 is ok w/ the declaration, but complains when you try to instantiate it with an initializer list – considering the struct a non-aggregate type. VS2008 and g++ are perfectly happy.
[Apologies.. I just realized that the line #s in the errors are wrong because I stripped some commented-out lines before posting.]
Microsoft’s definition of aggregates is here: http://msdn.microsoft.com/en-us/library/0s6730bb.aspx. It’s not apparent to me that const members would make a struct non-aggregate, but maybe they did back in 2003.
It also appears that the latest Borland (Embarcadero) compiler treats this as a warning rather than an error: http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/wrnmembnocons_xml.html .
So, 2 questions I guess:
Why the variance in compilers? Is the standard ambiguous on this point?
Any workarounds? Given that I'm stuck w/ the compiler version and the header file, I don't see any.
Thanks!
The standard is pretty clear. Having a const member doesn't bar a class from being an aggregate.
8.5.1 [dcl.init.aggr]
An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1), no private or protected non-static data members (clause 11), no base classes (clause 10), and no virtual functions (10.3).
It is legal to copy-intialize a const object and this is the initialization that aggregate initialization performs on the members of the aggregate. The restrictions on not naming a const object with no user-declared constructor in the mem-initializer-list in 12.6.2 apply only to initialization by a constructor which doesn't apply because aggregate initialization happens instead.
As to why the older compilers fail, I don't know. I can only say that they don't conform to the standard in this respect.
Question 1:
Compilers published at different times implement different versions of C++. They are different approximations to a standard. All have vendor-specific "additions", i.e. allow non-portable code.
BCC 5.2 was created before first C++ standard (ISO 14882:1998).
VC++2003 is close to ISO 14882:1998, but also has some deficiencies, especially concerning templates.
VC++2008 nearly implements ISO 14882:2003.
Question 2:
Try to get a modern compiler to your legacy system, if it's 32bit Windows.
Try to compile on a modern machine and deploy the executable on your legacy system.
If the legacy system is 16bit Windows, I don't see a solution.
On g++ 4.6.1 on the same Linux machine, -Wall -ansi -pedantic does not bring up any warnings.
This point might not have been so high on compiler writers' agenda. It looks so to me, looking at VS2003's and VS2008's behaviours, and, looking at g++ 3.3.3's behaviour you posted and g++ 4.6.1's behaviour I observed.
Can you consider changing the const to a private:, non const? That way, you will still have some control over who writes to it by not exporting a setter for it while, not generating compiler errors.
Just to add some extra information on the subject for MSVC and G++, and echoing exactly what #Charles Bailey, and #René Richter said; Its acceptable C++03, and C++11 but depending on the age and implementation state of your compiler you are going to get differing errors if at all.
Actually I tried Dinkum's online test compilers for EDG, MSVC2008, and G++ and all compile the sample you gave but NOT the sample I give below.
So in short fully initialized structs with const members will successfully compile on MSVC2008 and G++4.5, however of the tested compilers none can compile "partially initialized" (or partially aggregate initialized) POD structures even though that too is allowable in the C++ standard - I even contacted some G++ bug maintainers to make sure I was reading that standard correctly and they confirmed it should work even in current C++03 compilers.
You can see related bugs for this on both GNU Bugzilla and over at Microsoft's Visual Studio help pages which was actually linked from this other stackoverflow article titles "why do I get this warnings in Visual Studio when building a struct? which is also related to Microsoft's Error C3852 as a known behavior of even MSVC2010
// All sections refer to Draft C++03 (brackets refer to draft C++11)
//
// 3.9.3 CV (const/volatile) definition as "const data-type [= optional init]"
// 7.1.5.1/1 The cv-qualifiers [ 7.1.6.1/1 in C++11 ]
// "init-declarator-list of the declaration shall not be empty"
const int constval = 10 ;
// HOWEVER:
// 7.1.5.1/2 The cv-qualifiers [ 7.1.6.1 in C++11 ]
// [Note: as described in 8.5, the definition of an object or subobject
// of const-qualified type must specify an initializer or be subject to
// default-initialization. ]
// 8.5 Initializers
// 8.5/9 (C++11 8.5/11)
// Otherwise, if no initializer is specified for a non-static
// object, the object and its sub-objects, if any, have an indeterminate
// initial value(*90); if the object or any of its sub-objects are of
// const-qualified type, the program is ill-formed.
//
// *90: This does not apply to aggregate objects with automatic storage
// duration initialized with an incomplete brace-enclosed initializer list
// see 8.5.1.
// [ C++11 this sub-clause has been removed, however the list-initializer section
// pretty much covers the same topic - see 8.5.1/7 below ]
//
// 8.5.1 Aggregate definition
// 8.5.1/7 (C++11 8.5.1/7)
// If there are fewer initializers in the list than there are members in the
// aggregate, then each member not explicitly initialized shall be
// value-initialized (8.5).
//
// 8.5/5 value initialization
// 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 T has no accessible default constructor)
// ...
// otherwise, the object is zero-initialized
//
// 8.5/5 zero initialization
// if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
//
// POD type
struct A {
int n ;
const int m ; // "const" causes failure in MSVC to make default constructor
} ;
// Example of non-POD
struct B {
int bbb ;
B(){}
} ;
#include <stdio.h>
int main() {
// C++03/11 ill formed code, fails as expected
const int c0 ; // per 7.1.5.1 "not allowed to be default init"
// OK
const int c = *new int ; // A default initialized constant
const int c2 = *new int(); // A zero-init, above is DEFAULT-INIT
printf( "c: %i\n", c ) ; // should be an undef-value
printf( "c2: %i\n", c2 ) ; // should be 0
// OK ; Array example making sure it works
const int aa[5] = {}; // all items value->zero-initialized per the above 8.5.1/7
printf( "aa: %i %i %i\n", aa[0], aa[2], aa[4] ) ;
// C++03/11 ill formed code, no initializer (G++/MSVC should fail)
A a0 ; // Correct error - no default constructor or initializer (8.5/9)
// C++03/11 correctly formed code, full initializer list (G++/MSVC should pass)
A a1 = {1,2}; // Explicit initialization OK, G++/MSVC pass
// C++03/11 correctly formed code; copy initialized from a value-initialized A()
A a2 = A(); // G++ OK, MSVC FAIL
// C++03/11 correctly formed code; aggregate partial intializer (8.5.1/7 agg list init)
A a3 = {}; // G++/MSVC FAIL
A a4{}; // C++11 only - doesnt work in G++ (didnt try MSVC2010)
printf( "a0.m=%i\n", a0.m ) ; // a0 should not exist due compile errors
printf( "a1.m=%i\n", a1.m ) ; // a1.m should be 2
printf( "a2.m=%i\n", a2.m ) ; // a2.m should be 0
printf( "a3.m=%i\n", a3.m ) ; // a3.m should be 0
// C++03/11 correctly formed code; user-default constructor supplied.
const B bee1 ; // Default constructor marks bbb as "initialized"
const B bee2 = {} ; // CORRECTLY flagged error; init of non-aggregate
printf( "%i\n", bee1.bbb ) ;
}