I have a simple structure below which is used in constant expressions with aggregate initialization syntax. Now, I would like to make test::c optional thanks to NSDMI but this triggers an error for the default constructor in constant expression context with gcc and clang.
I don't understand why implicit default constructor default-initializes all members in first case but not with NSDMI.
There is no error if I set default value to all members but this is not what I want.
Moreover, I cannot declare a default constructor because the structure will no longer be an aggregate.
How the default contructor is implicitly generated in both case ? Is there a standard rule ?
Is there a way to fix the error and conserve the warning [-Wmissing-field-initializers] ?
#include "debug.h"
struct test {
//OK
/* int a, b, c; */
/* int a=0, b=0, c=3; */
//ERROR
int a, b, c=3;
};
int main(){
const test val1 = test(); //{0,0,3}
constexpr test val2 = test(); //error: call to non-'constexpr' function 'test::test()'
constexpr test val3 = {}; //{0,0,3}
constexpr test val4 = {1}; //warning: missing initializer for member 'test::b'
constexpr test val5 = {1,2}; //{1,2,3}
}
Output :
test.cpp: In function 'int main()':
test.cpp:13:32: error: call to non-'constexpr' function 'test::test()'
constexpr test val2 = test();
^
test.cpp:3:8: note: 'test::test()' is not usable as a 'constexpr' function because:
struct test {
^~~~
test.cpp:8:9: note: defaulted default constructor does not initialize 'int test::a'
int a, b, c=3;
^
test.cpp:8:12: note: defaulted default constructor does not initialize 'int test::b'
int a, b, c=3;
^
test.cpp:15:29: warning: missing initializer for member 'test::b' [-Wmissing-field-initializers]
constexpr test val4 = {1};
^
Thank you for your help.
Related
I have the following code
#include <iostream>
using namespace std;
struct S{
int a = 0;
S() = default;
};
union U {
S s;
int i;
};
int main() {
U u;
u.s.a = 1;
return 0;
}
However, it can't compile and issues the following errors
prog.cpp: In function ‘int main()’:
prog.cpp:15:4: error: use of deleted function ‘U::U()’
U u;
^
prog.cpp:9:7: note: ‘U::U()’ is implicitly deleted because the default definition would be ill-formed:
union U {
^
prog.cpp:10:4: error: union member ‘U::s’ with non-trivial ‘constexpr S::S()’
S s;
^
However when I modify the definition of U by adding a default constructor, it compiles.
union U {
S s;
int i;
U() {
}
};
My question is why the code can't compile without a given default constructor? Do we have something in the C++ standard to explain this?
My guess is C++ prohibits a implicit default constructor if a union has non-trivial member. And a class with in-class initialization members is non-trivial. Am I right?
Union cannot contain "non trivial" members: see here. "trivial" means that the member should do nothing on its constructor. Setting a to 0 is doing something, so the union cannot contain this member.
Your union has two members. At any time, one of the two members is active, and a constructor must make one of them active, but a default constructor cannot
know which one.
I am using the below code and getting error. I don't understand why I am getting this error.
prog.cpp: In function ‘int main()’:
prog.cpp:15:44: error: could not convert ‘{"foo", true}’ from
‘<brace-enclosed initializer list>’ to ‘option’
option x[] = {{"foo", true},{"bar", false}};
^
prog.cpp:15:44: error: could not convert ‘{"bar", false}’ from
‘<brace-enclosed initializer list>’ o ‘option’
The code
#include <iostream>
#include <string>
struct option
{
option();
~option();
std::string s;
bool b;
};
option::option() = default;
option::~option() = default;
int main()
{
option x[] = {{"foo", true},{"bar", false}};
}
When you provide† the default constructor and destructor, you are making the struct be a non-aggregate type, hence aggregate initialization is not possible.
However, you can check if a type is an aggregate using the standard std::is_aggregate_v trait. (Since c++17).
See here for your case. It is not an aggregate, as you provided† those constructors.
You have the following three ways to make this work:
Remove the constructors and you are good to go.
struct option
{
std::string s;
bool b;
};
Default the constructors inside the struct (i.e. declaring†).
struct option
{
std::string s;
bool b;
option() = default;
~option() = default;
};
Otherwise, you need to provide a suitable constructor in your struct.
struct option
{
std::string mStr;
bool mBool;
option(std::string str, bool b)
: mStr{ std::move(str) }
, mBool{ b }
{}
// other constructors...
};
† The following post explains when the constructor will be defaulted, when it is considered as user-declared and user-provided clearly: (Credits #NathanOliver)
C++ zero initialization - Why is `b` in this program uninitialized, but `a` is initialized?
In the following code, I want to use the default constructor {.data = value}, because I want my class to be POD. I don't understand the error message I get on compilation (llvm or gnu, c++11):
#include <type_traits>
class a {
char data;
static inline a create(char c) { return {.data = c}; } // this fails
static inline a create2(char c) { a x; x.data = c; return x; } // this is OK
public:
void init(char c) { *this = create(c); }
};
int main() {
a s;
s.init('x');
return std::is_pod<a>::value;
}
with error message
t.cc:5:43: error: no matching constructor for initialization of 'a'
static inline a create(char c) { return {.data = c}; }
^~~~~~~~~~~
t.cc:3:7: note: candidate constructor (the implicit copy constructor) not viable: cannot convert
argument of incomplete type 'void' to 'const a &'
Can some kind soul explain to me why the type of a is incomplete when I want to use it, and why it's treated as void?
You cannot aggregate initialize a private member.
From https://en.cppreference.com/w/cpp/language/aggregate_initialization
An aggregate is one of the following types: ... class type (typically, struct or union), that has no private or protected non-static data members
Since a is a class, not a struct, data is private.
Declare data to be public, or declare the type to be a struct to default it to public.
Then replace static inline a create(char c) { return {.data = c}; }
with static inline a create(char c) { return a { c }; }
per https://en.cppreference.com/w/cpp/language/list_initialization
direct-list-initialization (2)
I have encountered a situation in c++ where initializing an anonymous union member variable fails when I use initializer list with designators inside a member initializer list of a class constructor.
Following snippet might make things clearer:
class MyClass {
public:
MyClass() : my_union({.outer_field = 123}) {}
union {
struct {
int a: 16;
int b: 16;
} inner_field;
int outer_field;
} my_union;
};
Compiler gives the following error:
test.cpp: In constructor ‘MyClass::MyClass()’:
test.cpp:6:44: error: no matching function for call to ‘MyClass::<anonymous union>::._6(<brace-enclosed initializer list>)’
MyClass() : my_union({.outer_field = 123}) {}
^
test.cpp:7:9: note: candidate: MyClass::<anonymous union>::<constructor>()
union {
^
test.cpp:7:9: note: candidate expects 0 arguments, 1 provided
test.cpp:7:9: note: candidate: constexpr MyClass::<anonymous union>::<constructor>(const MyClass::<anonymous union>&)
test.cpp:7:9: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘const MyClass::<anonymous union>&’
test.cpp:7:9: note: candidate: constexpr MyClass::<anonymous union>::<constructor>(MyClass::<anonymous union>&&)
test.cpp:7:9: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘MyClass::<anonymous union>&&’
However, the following code compiles with no problem;
union myunion {
struct {
int a: 16;
int b: 16;
} inner_field;
int outer_field;
} my_union;
myunion m = {.outer_field = 123};
I can get around the problem by invoking the implicit constructor of union (as the error message suggests) like:
class MyClass {
public:
MyClass() : my_union({123}) {}
union {
struct {
int a: 16;
int b: 16;
} inner_field;
int outer_field;
} my_union;
};
But why does the initializer list with designators don't work within a member initializer list?
The g++ used to compile is version 5.4.0.
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Technically the example you give of compiling is not valid c++, but uses a GCC extension to allow C99 designated intializers. The case in question:
myunion m = {.outer_field = 123};
However, this is not using an initializer list, but uniform initialization. To get the same result in your code that you had as not compiling, just remove the outer parentheses:
class MyClass {
public:
MyClass() : my_union{.outer_field = 123} {}
union {
struct {
int a: 16;
int b: 16;
} inner_field;
int outer_field;
} my_union;
};
Instead of trying to pass an initializer list to the union's constructor, this performs uniform initialization. Keep in mind that it still is using a GCC extension, and would generate a warning if you passed -pedantic to the compiler.
EDIT:
Also as a warning, when using uniform-initialization, unions always initialize the first member. Structs will zero initialize any missing values. So your first example of the constructor that compiles:
MyClass() : my_union({123}) {}
This actually sets inner_field to the active field in the union, then a will be 123 and b will be 0. Here is an example. See here for more details about initializing unions.
please have a look at this program and the error it is generating:
#include <iostream>
using namespace std;
class A
{
public:
virtual void f(){}
int i;
};
class B : public A
{
public:
B(int i_){i = i_;} //needed
B(){} //needed
void f(){}
};
int main()
{
//these two lines are fixed(needed)
B b;
A & a = b;
//Assignment 1 works
B b1(2);
b = b1;
//But Assignment 2 doesn't works
B b2();
b = b2; // <-- error
}
upon compilation, I get the following error:
$ g++ inher2.cpp
inher2.cpp: In function ‘int main()’:
inher2.cpp:32:10: error: invalid user-defined conversion from ‘B()’ to ‘const B&’ [-fpermissive]
inher2.cpp:14:6: note: candidate is: B::B(int) <near match>
inher2.cpp:14:6: note: no known conversion for argument 1 from ‘B()’ to ‘int’
inher2.cpp:32:10: error: invalid conversion from ‘B (*)()’ to ‘int’ [-fpermissive]
inher2.cpp:14:6: error: initializing argument 1 of ‘B::B(int)’ [-fpermissive]
Can you help me find the problem? thank you
Your "B b2();" is the 'vexing parse' problem of C++ (see here - the 'most vexing parse' takes the ambiguous syntax further).
It looks to the C++ compiler that you are declaring a function (a pre-declaration).
Check it out:
int foo(); //A function named 'foo' that takes zero parameters and returns an int.
B b2(); //A function named 'b2' that takes zero parameters and returns a 'B'.
When you later do:
b = b2;
It looks like you are trying to assign a function (b2) to a variable (b).
To call a constructor with zero parameters, call it without the parentheses and you'll be fine:
B b2;
For more information, see:
Understanding 'most vexing parse' - why allow ambiguous syntax?
Most vexing parse: why doesn't A a(()); work?
B b2();
It is a function declaration, not a variable declaration!
The function name is b2 which takes no argument, and returns object of type B.
Search for vexing parse in C++.