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.
Related
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.
I am getting no appropriate default constructor available error with the following simple piece of code:
class A
{
public:
const string cs ;
};
void main()
{
A a;
return;
}
If I remove the const from string then code compiles fine. I can not understand why the default constructor is not getting created by compiler? And what is the deal with const string member variable?
I am working on VS2008.
As mentioned in the comments, const variables cannot be left unitialized in C++. There are two ways you can initialize your variable. In both cases, the content of the string can never be modified (as this is what const means).
1) In the class declaration. This method is useful only if you always want this string to have the same value across all of your objects. It is very inflexible and if you find yourself using it you should probably declare the variable as static.
class A
{
const string cs = "value of cs";
};
2) assign it in the constructor, using constructor chaining. This is much more flexible and idiomatic.
class A
{
const string cs;
public:
A() : cs("value of cs")
{
}
};
Note that this can be used with arguments, eg
A(string s) : cs(s) //initializes cs to the value of s
Your error probably arises from the compiler trying to find the second option.
Since C++17 it is legal to have a const member with no initializer -- if that member has a default constructor which initializes itself. void main() is still incorrect though!
So here is some valid code:
#include <string>
struct A { const std::string cs; };
int main()
{
A a;
}
The string a.cs is an empty string which is const.
If using an older compiler you will have to give a redundant initializer for cs.
Is the following code safe or does it lead to undefined behavior in C++03?
class Aries {
public:
Aries() : Taurus("foo") , Leo(Taurus + "bar") {}
private:
string Taurus;
const string Leo;
};
This code is obviously a stripped down version of my actual problem.
Edit Taurus has been intentionally declared before Leo in the class declaration. I am aware the initialization happens in the order of initialization in class declaration (not the order in the initializer list.)
Just to clarify an alternative I mentioned in the comments:
class Aries {
public:
friend Aries makeAries() {
string Taurus = "foo"; // easier to do computations here:
return Aries( Taurus, Taurus + "bar" );
}
private:
Aries( string const &inTaurus, string const &inLeo )
: Taurus(inTaurus) , Leo(inLeo) {}
string Taurus;
const string Leo;
};
Aries makeAries(); // must declare a friend factory outside the class
This isn't a universal solution or a really desirable idiom, but can still be helpful and relatively clean.
Yes, you can safely use members that are initialized before the member you are initializing.
Note that the order of initialization is the order of declaration in the class definition.
I have a custom class that I want to behave like a built-in type.
However I have noticed that you can initialise a const variable of that class without providing an initial value. My class currently has an empty default constructor.
Here is a comparison of int and my class foo:
int a; // Valid
int a = 1; // Valid
const int a = 1; // Valid
const int a; // Error
foo a; // Valid
foo a = 1; // Valid
const foo a = 1; // Valid
const foo a; // Should cause an error, but it compiles
As you can see I need to prevent
const foo a;
from compiling.
Any ideas from C++ gurus?
It compiles only if it has a default constructor, and it compiles because it has it, which means that it is initialized. If you don't want that line to compile, just disable the default constructor (will also make foo a; an error as an unwanted side effect). Without a definition of foo or what you want to do, this is as far as I can get.
I don't think there is any way of achieving what you want (i.e. allow the non-const variable to be default initialized, while having the const version fail compilation and allowing the other use cases --that require providing constructors)
The rules of C++ simply say that default-initialization (e.g. new T;) and value-initialization (e.g. new T();) are the same for objects of class type, but not for objects of fundamental type.
There's nothing you can do to "override" this distinction. It's a fundamental part of the grammar. If your class is value-initializable, then it is also default-initializable.
There is a sort-of exception for classes without any user-defined constructors: In that case, initialization of members is done recursively (so if you default-init the object, it tries to default-init all members), and this will fail if any of the class members are themselves fundamental, or again of this nature.
For example, consider the following two classes:
struct Foo { int a; int b; };
struct Goo { int a; int b; Goo(){} };
//const Foo x; // error
const Goo y; // OK
The implicit constructor for Foo is rejected because it doesn't initialize the fundamental members. However, y is happily default-initialized, and y.a and y.b are now "intentionally left blank".
But unless your class doesn't have any user-defined constructors, this information won't help you. You cannot "forward" the initialization type to a member (like Foo() : INIT_SAME_AS_SELF(a), b() { }).
I want to know what's difference in the following two class.
example 1:
class A
{
string name;
public:
A(const char* _name):name(_name){}
void print(){cout<<"A's name:"<<name<<endl;}
};
example 2:
class A
{
string name;
public:
A(const char* _name){name(_name);}
void print(){cout<<"A's name:"<<name<<endl;}}
why the example 1 is passed and the last one is wrong?
Thanks
In example 1 you initialize the string with the given value right away.
In example 2 you create an empty string first and assign it later on.
Despite some performance differences and ignoring possible differences due to copy constructor handling etc. it's essentially the same result.
However once you use a const member you'll have to use example 1's way to do it, e.g. I usually create unique IDs the following way:
class SomeObject
{
static unsigned int nextID = 0;
const unsigned int ID;
SomeObject() : ID(nextID++)
{
// you can't change ID here anymore due to it being const
}
}
The first example is an actual initialization. It has a number of advantages, including being the only way to set up const members, and having proper exception-safety.
The second example is not valid C++, AFAIK. If you had instead written name = name_, then this would just be normal assignment. Of course, this isn't always possible; the object might be const, or not have an assignment operator defined. This approach could also be less efficient that the first example, because the object is both default-initialized and assigned.
As for why the initializer list is before the constructor body; well, that's just the way the language has been defined.
That's just how the language is defined. The member initializers should be placed before the body of the constructor.
In the first example the member name is initialized with a ctr getting char * as parameter.
In the second case it is initialized with a default ctr at first and it gets value by the assignment operator (operator=) later. That's why it is wrong with your case that it is already constructed there so you can not use the ctr once again you could just use the assignment operator.
The reason is that name lookup works different in initializer lists and function bodies:
class A
{
std::string foo; // member name
public:
A(const char* foo) // argument name
: foo(foo) // member foo, followed by argument foo.
{
std::cout << foo; // argument foo.
}
};
If the initializer list was inside the function body, there would be an ambiguity between member foo and argument foo.
The motivation behind the initialization list is due to const field holding a object by value (as opposed to reference/pointer field).
Such fields must be initialized exactly once (due to their const-ness). If C++ didn't have initialization list then a ctor would look something like:
class A {
public:
const string s;
const string t;
A() {
// Access to either s or t is not allowed - they weren't initialized
s = "some-string";
// now you can access s but you can't access t
f(*this);
t = "some other string";
// From now you can access both ...
}
}
void f(A& a) {
// Can you access a.s or a.t?
cout << a.s << a.t;
}
Without an initialization list a ctor can pass a partially-initialized object of type A to a function, and that function will have no way of knowing which fields are initialized yet. Too risky and very difficult for the compiler/linker to check.