I spotted a "mistake" in my code today... But I'm not sure if the "mistake" actually changes the resulting compiled code.
Consider initialization of the following reference to a double, x.
double &x{*_data->x_double}; // x_double is a member variable of the struct
// _data, it is a pointer to another double
// eg, I have this somewhere else...
struct data
{
double *x_double;
};
data *_data = new data; // edit: duh, this has to be a pointer...
double another_x = 10.0;
_data->x_double = &another_x; // edit: pointer here too!
However I made the following "mistake"... (notice the extra = sign)
double &x={*_data->x_double};
This code is a minimal example copied from my actual code, in which I don't have references to doubles, but references to large objects such as std::vector's.
The reason for the reference variables is that they are used in an algorithm, and the variable names are very long, so I create shorted aliases for those variables using references. Hope it makes sense why I did that...
So, I've "corrected" the "mistake" but has my output compiled code actually changed?
They're both list initialization. For your cases:
double &x={*_data->x_double};
Is :
6) initialization of a named variable with a braced-init-list after an
equals sign
And
double &x{*_data->x_double};
Is :
1) initialization of a named variable with a braced-init-list (that
is, a possibly empty brace-enclosed list of expressions or nested
braced-init-lists)
Their effects are in this case exactly the same.
Related
Long time java programmer here, new to C++. I have been working with C-style "traditional" arrays (similar to arrays in java). I understand in C++ we can create a simple array as follows:
Person people[3];
The contents of this array is essentially uninitialized junk values. When I print out the contents of each element in the array, I (believe) am getting the memory address of each element.
for(int i = 0; i < 3; i++){std::cout << &person[i] << std::endl;}
Results:
//Note, I get different results here when I use an enhanced for loop vs an iterator for loop. Weird.
00EFFB6C
00EFFBA8
00EFFBE4
Now, here is the part I have failed to get a clear explanation on. I create a pointer to one of the elements in the array. I then ask for some value back from that pointer. In java, I would expect to get a null pointer, but in C++, that is not happening.
Instead, I get the default value, as though this element is initialized:
Person* person1Ptr = &people[0];//Points to an uninitialized value
std::cout << person1Ptr->getFirstName() << std::endl;//Output: "Default First Name", expected nullptr
When I try to get the first name of an element using a reference, this doesn't work (presumably because the value doesn't exist).
Full paste of code: https://pastebin.com/cEadfJhr
From my research, C++ does NOT fill arrays with objects of the specified type automagically.
How is my person1Ptr returning a value?
I believe the problem stems from this misconception :
From my research, C++ does NOT fill arrays with objects of the specified type automagically.
C++ objects have value semantics. Defining a local variable of type T concretely creates a unique instance of that type, it is not a handle to a potential T. The expression Foo f; is conceptually equivalent to the Java expression Foo f = new Foo();. Additionally, value semantics means assignment usually implies a copy. The C++ expression Foo f; Foo g = f; is conceptually equivalent to the Java expression Foo f = new Foo(); Foo g = f.Clone();.
In the case of an array, defining a local variable Foo f[3]; immediately creates three instances of Foo as elements of the f array. Your misconception may come from the fact that creating an object in C++ does not imply that it has been initialized. An object can exist in an uninitialized state. For example int i; create an int object identified by i but its value is indeterminate. In the case of int i[3]; you would have an array of three int each with indeterminate values.
The rules for initialization are very complicated in C++. In the case of Person people[3]; you have an array that is default initialized.
You are initializing an object of type Person[3]. According to default initialization rules :
if T is an array type, every element of the array is default-initialized;
That means each Person gets its own default initialization. To see what happens, consider that T is Person :
if T is a class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;
So each Person's default constructor will be called to initialize that element. You end up with Person people[3]; defining three default constructed Person objects with default initial values.
I have this class in C++ and want to replicate it in Julia.
class Clock
{
double epsilon;
double dt;
int64_t timestep;
double t;
Clock(double _epsilon=1e-14) : epsilon(_epsilon)
};
So far I have
mutable struct Clock
epsilon::Float64
dt::Float64
timestep::Int64
t::Float64
#Need the constructor#
end
The closest to C++ behaviour I believe would be the following:
mutable struct Clock
epsilon::Float64
dt::Float64
timestep::Int64
t::Int64
Clock(epsilon=1e-14) = new(epsilon)
end
This leaves the rest of the fields incomplete. Since they have so-called bits types, they are actually initialized, but with undefined values (as opposed to C++, where they are uninitialized, IIUC), so accessing the fields without assignment does not error (it would be for non-bitstypes, such as arrays).
The struct has to be mutable, because otherwise you couldn't change the fields after you constructed the value and were left with a bunch of garbage forever.
But let it be said that incomplete initialization is not particularly idiomatic, or at least to be avoided whenever possible. I'd rather write the type as follows:
struct Clock
epsilon::Float64
dt::Float64
timestep::Int64
t::Int64
end
Clock(epsilon=1e-14) = Clock(epsilon=epsilon, 0.0, 0, 0)
(or with whatever defaults make sense for you), and stick to immutable functions manipulating it. That second variant uses an outer constructor, which is basically just a method on the type (special things like new and incomplete initialization are available only in inner constructors). This outer constructor call the default-provided inner constructor.
I just stumbled upon a behavior which surprised me:
When writing:
int x = x+1;
in a C/C++-program (or even more complex expression involving the newly created variable x) my gcc/g++ compiles without errors. In the above case X is 1 afterwards. Note that there is no variable x in scope by a previous declaration.
So I'd like to know whether this is correct behaviour (and even might be useful in some situation) or just a parser pecularity with my gcc version or gcc in general.
BTW: The following does not work:
int x++;
With the expression:
int x = x + 1;
the variable x comes into existence at the = sign, which is why you can use it on the right hand side. By "comes into existence", I mean the variable exists but has yet to be assigned a value by the initialiser part.
However, unless you're initialising a variable with static storage duration (e.g., outside of a function), it's undefined behaviour since the x that comes into existence has an arbitrary value.
C++03 has this to say:
The point of declaration for a name is immediately after its complete declarator (clause 8) and before its initializer (if any) ...
Example:
int x = 12;
{ int x = x; }
Here the second x is initialized with its own (indeterminate) value.
That second case there is pretty much what you have in your question.
It's not, it's undefined behavior.
You're using an uninitialized variable - x. You get 1 out of pure luck, anything could happen.
FYI, in MSVS I get a warning:
Warning 1 warning C4700: uninitialized local variable 'i' used
Also, at run-time, I get an exception, so it's definitely not safe.
int x = x + 1;
is basically
int x;
x = x + 1;
You have just been lucky to have 0 in x.
int x++;
however is not possible in C++ at a parser level! The previous could be parsed but was semantically wrong. The second one can't even be parsed.
In the first case you simply use the value already at the place in memory where the variable is. In your case this seems to be zero, but it can be anything. Using such a construct is a recipe for disaster and hard to find bugs in the future.
For the second case, it's simply a syntax error. You can not mix an expression with a variable declaration like that.
The variable is defined from the "=" on, so it is valid and when it is globally defined, it is initialized as zero, so in that case it is defined behavior, in others the variable was unintialized as as such still is unitialized (but increased with 1).
Remark that it still is not very sane or useful code.
3.3.1 Point of declaration 1 The point of declaration for a name is immediately after its complete declarator (clause 8) and before its
initializer (if any), except as noted below. [ Example: int x = 12; {
int x = x; } Here the second x is initialized with its own
(indeterminate) value. —end example ]
The above states so and should have indeterminate value, You are lucky with 1.
Your code has two possiblities:
If x is a local variable, you have undefined behavior, since you use the value of an object before its lifetime begins.
If x has static or thread-local lifetime, it is pre-initialized to zero, and your static initialization will reliably set it to 1. This is well-defined.
You may also wish to read my answer that covers related cases, including variables of other types, and variables which are written to before their initialization is completed
This is undefined behaviour and the compiler should at least to issue a warning. Try to compile using g++ -ansi .... The second example is just a syntax error.
I ment I want to copy the value of a constant primitive type, and assigned to a reference. I want to do so whthout make it in the heap is that posible?
make some thing like this:
const unsigned long rawnan[2]={0xffffffff, 0x7fffffff};
const double NaN = *( double* )rawnan;
Class{
float &_f;
inline Class( ):_f(float(NaN)){
}
}
and I want to know if when:
Class{
float &_f;
inline Class( float F):_f(float(F)){
}
}
if I actually making a copy of the of the variable F or not (I know could reference directly to F but I think it scope will end at the end of the constructor then I need to make a copy of it value.)
(if the code not compile, it is because is a pseudo-code from a real code, the fundament question is the important, I hope is understandable)
(I do the deffinition of NaN so, because is the best way so far I know to do it and be compatible with VC++ and G++)
Here's how:
float first = 3.14159265358979324f;
float second = first;
Neither of your code snippets is correct.
float(NaN) and float(F) are temporary objects. You bind them to the reference data member _f, but this does not extend their lifetime. So, by the time you come to use the reference _f it will be dangling.
If you want to take a copy of a float, use a float data member, not a float&. If you want a reference data member, you must make sure it refers to something that will still exist when the reference is used.
if the code not compile, it is because is a pseudo-code from a real code
The correct way to deal with this is to write a short, complete program including the code you want to ask about, check that it compiles, and then ask a question about your complete program. There are at least two compilation problems with the code you posted. Your real code (if it compiles in g++) cannot possibly initialize the reference the way that you have done in your sample code.
I understand from the answer to this question that values of global/static uninitialized int will be 0. The answer to this one says that for vectors, the default constructor for the object type will be called.
I am unable to figure out - what happens when I have vector<int> v(10) in a local function. What is the default constructor for int? What if I have vector<int> v(10) declared globally?
What I am seeing is that vector<int> v(10) in a local function is resulting in variables being 0 - but I am not sure if that is just because of my compiler or is the fixed expected behaviour.
The zero initialization is specified in the standard as default zero initialization/value initialization for builtin types, primarily to support just this type of case in template use.
Note that this behavior is different from a local variable such as int x; which leaves the value uninitialized (as in the C language that behavior is inherited from).
It is not undefined behaviour, a vector automatically initialises all its elements. You can select a different default if you want.
The constructor is:
vector( size_type, T t = T() )
and for int, the default type (returned by int()) is 0.
In a local function this:
int x;
is not guaranteed to initialise the variable to 0.
int x = int();
would do so.
int x();
sadly does neither but declares a function.
The constructor you are using actually takes two arguments, the second of which is optional. Its declaration looks like this:
explicit vector(size_type n, const T& value = T())
The first argument is the number of elements to create in the vector initially; the second argument is the value to copy into each of those elements.
For any object type T, T() is called "value initialization." For numeric types, it gives you 0. For a class type with a default constructor, it gives you an object that has been default constructed using that constructor.
For more details on the "magic parentheses," I'd recommend reading Michael Burr's excellent answer to the question "Do the parentheses after the type name make a difference with new?" It discusses value initialization when used with new specifically, but for the most part is applicable to value initialization wherever else it can be used.
By default, vector elements are zero-initialized and not default-initialized. Those are two different but related concepts:
zero-initialization is what is done for static objects not having an explicit initialization and what is done for a member given in the initialized list with an initializer of (). For basic types, the value used is 0 converted to the type.
default-initialization is what is done for not explicitly initialized non static variables and members. For basic types it stay uninitialized.
(And C++0X introduces value-initialization which is still different).
As mentioned by others, what happens is the zero initialization kicks in. I actually use that a lot in my code (outside of vectors and other classes):
some_type my_var = some_type();
This allows me to make sure that my variables are always properly initialized since by default C/C++ do not initialize basic types (char, short, int, long, float, double, etc.)
Since C++11, you also can do so in your class definitions:
class MyClass
{
...
int my_field_ = 123; // explicit initialization
int your_field_ = int(); // zero initialization
};
For vectors, the std library uses T(). Whatever T() is, it will use that default initialization. For a class, it calls the default constructor. For a basic type, it uses zero ('\0', 0, 0.0f, 0.0, nullptr`).
As mentioned by James McNellis and Nawaz, it is possible to set the value used to initialize the vector as in:
std::vector<int> foo(100, 1234);
That feature is also available when you resize your vector (if the vector shrinks, the default value is ignored):
foo.resize(200, 1234);
So that way you can have a default initialization value. However, it's a be tricky since you have to make sure that all your definitions and resize() calls use that default value. That's when you want to write your own class which ensures that the default value is always passed to the vector functions.
However, if you want to have a way to auto-initialize to a specific value, you can mix both features this way:
struct my_value {
int v = 123;
};
std::vector<my_value> foo(100);
// here foo[n].v == 123 for n in [0, 100)
This is my preferred way of dealing with this issue (i.e. if I don't want zero by default). It's an extra .v, but much less prone to mistakes and you don't need to know of the default value when you create a vector of my_value.
Also, for those who think this will be slow, it won't. The struct is like syntactic sugar as far as C++ is concerned. One optimized, it will be exactly the same as a simple std::vector<int> foo(100, 123).
The default initialization for an int type is to initialize it to 0.
This is true of most (if not all) primitive types: char will initialize to (char)0 (or '\0' if you prefer), float will initialize to 0.0f, and any pointer initializes to NULL. For other types, the parameterless constructor is invoked.
In general, the default initialization should happen pretty much whenever you aren't able to specify a constructor (or choose not to).