My problem is with the following code:
extern "C" struct CStruct {
char a;
char b;
};
class X {
CStruct cs;
public:
X(CStruct cs_arg) : cs{cs_arg} {}
X(CStruct cs, bool){
this->cs = cs;
}
};
Clang 3.4 (c++11) complains for the first constructor but not for the second one.
../st2.cpp:10:25: error: no viable conversion from 'CStruct' to 'char'
X(CStruct cs_arg) : cs{cs_arg} {}
^~~~~~
1 error generated.
How come it says conversion to char if the cs member is clearly a struct?
Can i make this kind of initalization work in the initialization list or must i do it in the function body? Why?
The real code uses a template for the class and it fails if the type is a simple POD struct. It should never handle anything more complex then POD structs.
You are using aggregate initialization.
You should have
cs {char1, char2} .
If you want initialiation from another struct, you should use
cs(cs_arg).
Or, if you don't want to make a copy constructor use
cs{cs_arg.a, cs_arg.b};
Are you trying to initialize cs? Then the code may be corrected like
X(CStruct cs_arg) : cs(cs_arg) {} // change cs_arg{cs} to cs(cs_arg)
Here the copy constructor will be invoked.
Related
Say I have a C style struct as follows:
typedef struct {
const struct {
int ac;
int bc;
} const_vars;
int x;
int y;
} myCStruct;
I can initialize a variable of the above type as follows:
myCStruct sVar = {{123,678},0,0};
Now I decide to have a std::map with the above:
std::map<int,myCStruct,std::less<int>,myAllocator<std::pair<int,myCStruct>>> myMap;
Doing this I get a compilation error:
error: call to implicitly-deleted default constructor of 'myCStruct'
myMap tried to instantiate myCStruct and ran up against a deleted default ctor. I understand the default ctor got deleted because of the const members of the struct. I tried changing the type to a C++ style struct, and defined a default ctor, but then I got stuck unable to initialize the variable myCStruct sVar as above, because the compiler couldn't find a matching ctor.
Q1. Would a matching ctor in this example take a std::initializer_list, int, and int as arguments?
Q2. This is a toy example, my actual use case is a bit more complex, and writing a matching ctor would be tedious (and seems like an overkill). I have more types like this, so I'll have to replicate this for all of them. The convenience of defining myCStruct sVar as shown above is unbeatable! Do I have an easy out?
I looked at a piece of code and I am trying to understand how it works and so here is a minimal working example
template <typename T>
class A
{
public:
A() : _mem(T()) {};
private:
T _mem;
};
The first thing I was not exactly clear about is the initialisation of _mem in the initialiser list. What is this technique(?) called? If I look in the debugger _mem is set to 0. If there is a c++11 way to do the same thing could I receive some comments on that?
This is just to safeguard against an uninitialized A::_mem when T is a POD type or a built-in type (like an int). Using a constructor initializer list of : _mem(T()) will default construct a T and initialize _mem with it. For example:
struct POD {
int num;
};
// ...
A<POD> a;
Here, a._mem would be unitialized (in this case, this means a._mem.num is unitialized.) The : _mem(T()) prevents that from happening and will initialize a._mem.num to 0.
However, since C++11 you can just use inline initialization instead:
template <typename T>
class A {
public:
A() {};
private:
T _mem{};
};
T _mem{}; will default-construct _mem, which in turn means POD::num will be default-constructed, which for an int means it will be zero-initialized.
If this has a name, I don't know it.
What's happening:
T() constructs a temporary T and Zero Initializes it.
_mem(T()) makes _mem a copy of the temporary T. Note: A modern compiler will almost certainly elide the copy and simply zero initialize _mem. This still requires that T be copy-or-moveable, so _mem(T()) and _mem() are not exactly the same.
The only relevant C++ 11 difference I can think of is you can use curly braces, _mem{T{}}, for List Initialization. Not useful here, but very useful in other circumstances.
I was under the assumption that a const member must be initialized in the class constructor.
Consider following code
class ABC
{
const string a;
const string b;
const string c;
public:
ABC( struct input in): a(in.a), b(in.b){}
};
I thought this would trigger a compile time error since explicit initialization for c is missing. However this compiles and gives empty string for c under VS2012. Is this correct behavior? (or special case for string?)
const member must be initialized when declared
The above holds true for only built-in and POD types.For Non-POD types, default constructor will be called which will initialize it.
std::string is a non-POD.
As mentioned in comments by #Dan, try changing const string c; to const int c;,the compiler will complain.
I would have initially expected the following code to compile:
#include <set>
using namespace std;
class A {
public:
set<int> st;
A(set<int> s) : st(s) {}
};
int main() {
A a = {1,2,3}; // Error, couldn't convert from initializer list
A b({1,2,3}); // Ok
}
I can't understand why the first construction fails but the second one succeeds. I tried to replicate the error without the use of initializer list but I couldn't do so.
I am using vc12, but it also fails on the microsoft online visual compiler (with a different error message and code to my compiler).
Should this compile?
If not why not?
EDIT:
The following does compile (EDIT: Only in visual studio and this is a bug.):
class A {
public:
int i;
A(int j) : i(i) {}
};
class B {
public:
A a;
B(A o) : a(o) {}
};
class C {
public:
B b;
C(B u) : b(u) {}
};
int main() {
C c = A(10); // Compiles just fine, but isn't this doing the same set of implicit conversions?
C v(A(10));
}
The two seem the same to me in terms of the number of implicit conversions.
Given this code:
#include <set>
using namespace std;
class A {
public:
set<int> st;
A(set<int> s) : st(s) {}
};
int main() {
A a = {1,2,3}; // Error, couldn't convert from initializer list
A b({1,2,3}); // Ok
}
The declaration of a, using the “=” syntax, is a copy initialization with a brace-enclosed list.
If A were a Plain Old Data type or an “aggregate” then the values in the list would be used to initialize the items of a in order, and the rest of the items, if any, would be zero-initialized.
If A instead had a constructor taking a suitable std::initializer_list argument, then that would have been used.
Those two possibilities are exhaustive. For example, if the constructor with std::set had been considered (it isn't considered), then that would have involved two implicit user-defined conversions to produce the temporary object on the right hand side of =, namely std:initializer_list → std::set, and then std:.set → A. And the rules of C++ limit an implicit conversion sequence to at most one user defined conversion.
And so, since class A is neither POD/aggregate nor a class with std::initializer_list construction, you get an error.
The declaration of b is direct initialization. Here the provided argument is used not to produce a temporary A, but to produce an argument acceptable to some A constructor. One such possibility is found: using the argument to produce a std::set.
When initializing an object with = the compiler implicitly creates a temporary object of your type and then copies it to your variable (although the temporary and copy may, and usually are, elided).
So your a object is theoretically equivalent to:
A a = A(std::set<int>({1, 2, 3}));
I'm not sure exactly why the behaviour differs but I think it's due to the fact that the compiler is allowed to only perform one user defined conversion implicitly. In this case it's probably considered two separate UDCs:
initializer list to std::set
std::set to A
In first case you are trying to construct your object using constructor which takes an std::initializer_list as single parameter (which is not implemented). In your second call a temporary std::set<int> instance is constructed and your A(set<int> s) constructor is called.
In order to fix it implement the missing constructor:
A(std::initializer_list<int> list) : st(list) { }
I am programming Arduino and I have trouble when using the following code
struct myStruct {
char variable1[10];
char variable2[10];
char variable3[10];
// Constructor
myStruct() : variable1({'\0'}), variable2({'\0'}), variable3({'\0'}) {}
};
since I get the following error:
expected primary-expression before '{' token
What is the problem? How can I solve it?
Note: The \0 is used for handling null terminated strings.
BTW: Why the following code works and the above does not?
struct myStruct {
char variable1[10];
char variable2[10];
char variable3[10];
} variable = {{'\0'}, {'\0'}, {'\0'}};;
Remove parens. Use braces only.
That is, instead of
variable1({'\0'})
write this,
variable1{'\0'} //removed parens!
If you follow that, your code would look like this:
myStruct() : variable1{'\0'}, variable2{'\0'}, variable3{'\0'} {}
Any compiler that supports C++11 should be able to compile it.
In C++03, write this:
myStruct() : variable1(), variable2(), variable3() {}
That should work for this particular case. That is all you have : value-initialization. C++03 doesn't give you much freedom.
In C++11, your code should work, although it has rather more brackets than strictly necessary.
In any C++, you could specify value-initialisation to zero-initialise the arrays:
myStruct() : variable1(), variable2(), variable3() {}
If you're being ultra-paranoid, and don't trust '\0' to be equivalent to zero, then you'd have to write to the arrays in the constructor body.
Why the following code works and the above does not?
Because it's always been possible to aggregate-initialise variables in a declaration. It's only since 2011 that it's been possible to do that in a member initialiser.
Assuming you just want to initialise the member variables to empty (C-style) strings, change:
// Constructor
myStruct() : variable1({'\0'}), variable2({'\0'}), variable3({'\0'}) {}
to:
// Constructor
myStruct() : variable1(""), variable2(""), variable3("") {}
Edit
Apparently some versions of gcc complain about this usage (see comments below) - this appears to be due to a bug in gcc.