Although I much enjoy the new features in C++11, sometimes I feel like I'm missing some of its subtleties.
Initializing the int array works fine, initializing the Element2 vector works fine, but initializing the Element2 array fails. I think the correct syntax should be the uncommented line, but none of the initialization attempts have succeeded for me.
#include <array>
#include <vector>
class Element2
{
public:
Element2(unsigned int Input) {}
Element2(Element2 const &Other) {}
};
class Test
{
public:
Test(void) :
Array{{4, 5, 6}},
Array2{4, 5},
//Array3{4, 5, 6}
Array3{{4, 5, 6}}
//Array3{{4}, {5}, {6}}
//Array3{{{4}, {5}, {6}}}
//Array3{Element2{4}, Element2{5}, Element2{6}}
//Array3{{Element2{4}, Element2{5}, Element2{6}}}
//Array3{{{Element2{4}}, {Element2{5}}, {Element2{6}}}}
{}
private:
std::array<int, 3> Array;
std::vector<Element2> Array2;
std::array<Element2, 3> Array3;
};
int main(int argc, char **argv)
{
Test();
return 0;
}
I've tried this on g++ 4.6.1 and 4.6.2 under MinGW.
How should I correctly go about initializing this array? Is it possible?
The correct way to go about this is Array{{4, 5, 6}}. You cannot omit braces when you initialize a member with aggregate initialization. The only time you can omit braces is in a declaration of the form
T t = { ... }
So in your case you have to type out all braces: One for the std::array itself, and one for the int array. For Array3, your syntax is correct too, since int can be converted to Element2 implicitly.
From the remaining commented ones, the Array3{{{4}, {5}, {6}}}, Array3{{Element2{4}, Element2{5}, Element2{6}}} and Array3{{{Element2{4}}, {Element2{5}}, {Element2{6}}}} work too, but are more wordy. However conceptionally the Array3{{{4}, {5}, {6}}} one produces the least amount of temporaries on implementations that don't do copy elision (I guess that's irrelevant, but still good to know), even less than the Array3{{4, 5, 6}} one, because instead of copy initialization you use copy list initialization for your Element2, which doesn't produce an intermediary temporary by design.
Related
Is there a way to initialize first few elements of a vector after defining the size of the vector like -
vector<int> vec (10);
This doesn't work and produces a compiler error -
vector<int> vec(10) {1,2,3};
For example with arrays we can do the same thing like -
int arr[5] {1,2,3}; // This will initialize the first 3 elements of the array to 1,2,3 and the remaining two to 0.
In short, no. Your can fill out the entire list of things you want to be in the vector:
vector<int> vec{1, 2, 3, 0, 0, 0, 0, 0, 0, 0};
Which will give you a vector of 10 elements.
Or, you can create the vector, then call resize to make it larger (filling the remaining elements with 0):
vector<int> vec{1, 2, 3};
vec.resize(10);
You generally don't need to do this kind of thing to vector though, because unlike array, you can extend vector as needed, after creation:
vector<int> vec{1, 2, 3};
vec.push_back(4);
There isn't a way to do it all in one line like you can with an array. You can use
vector<int> vec{1,2,3};
vec.resize(10);
but that does make the code a little less easy to use. Another option is to wrap that in a function like
template <typename T>
auto make_sized_vector(std::intializer_list<T> il, std::size_t size = 0)
{
const auto vec_size = std::max(size, il.size());
vector<T> vec; // create vector
vec.reserve(vec_size); // allocate all the storage needed
vec.assign(il); // assign the elements
vec.resize(vec_size); // set the rest to zero
return vec;
}
and then you can use that like
auto vec = make_sized_vector<int>({1, 2, 3}, 10);
If you are concerned about passing the std::intializer_list by value see why is `std::initializer_list` often passed by value? for why that really isn't a concern.
In case you want to initialize a vector the way you describe, all at once, so that it can become (e.g.) a const member, this is always possible in C++, with just a bit of ugliness and twisting. Let’s say you have a class:
struct SomeClass {
SomeClass(const std::vector<int> &start, int rest, std::size_t size);
const std::vector<int> some_vector_; // This is const!
};
What the constructor could look like:
SomeClass::SomeClass(const std::vector<int> &start, int rest, std::size_t size)
: some_vector_{[&start, rest, size] {
std::vector<int> some_vector;
some_vector.reserve(size);
some_vector.insert(some_vector.end(), start.begin(), start.end());
some_vector.insert(some_vector.end(), size - start.size(), rest);
return some_vector;
}()} {}
Basically the problem boils down to: How do I do “something procedural” in an initializer list? To which the answer is: You invoke a function that returns the desired type.
To test the construct above:
#include <cstdint>
#include <iostream>
#include <vector>
namespace { /* SomeClass stuff from above goes here. */ }
int main() {
SomeClass sc{{1, 2, 3}, 0, 10};
for (int i : sc.some_vector_) std::cout << i << '\n';
}
There are (of course) plenty of ways to make it (slightly) more efficient if needed, such as
a templated variadic constructor to create the initial part of the vector,
a templated perfect-forwarding constructor to benefit from R-value containers, and
as a combined benefit of the above, arbitrary iterable containers as inputs and as the const member.
I'm getting a strange compiler error when trying to create constexpr std::string and std::vector objects:
#include <vector>
#include <string>
int main()
{
constexpr std::string cs{ "hello" };
constexpr std::vector cv{ 1, 2, 3 };
return 0;
}
The compiler complains that "the expression must have a constant value":
Am I missing something? I am using the latest Microsoft Visual Studio 2019 version: 16.11.4, and the reference (https://en.cppreference.com/w/cpp/compiler_support) states that constexpr strings and vectors are supported by this compiler version:
I have also tried the constexpr std::array, which does work. Could the issue have anything to do with the dynamic memory allocation associated with vectors?
Your program is actually ill-formed, though the error may be hard to understand. constexpr allocation support in C++20 is limited - you can only have transient allocation. That is, the allocation has to be completely deallocated by the end of constant evaluation.
So you cannot write this:
int main() {
constexpr std::vector<int> v = {1, 2, 3};
}
Because v's allocation persists - it is non-transient. That's what the error is telling you:
<source>(6): error C2131: expression did not evaluate to a constant
<source>(6): note: (sub-)object points to memory which was heap allocated during constant evaluation
v can't be constant because it's still holding on to heap allocation, and it's not allowed to do so.
But you can write this:
constexpr int f() {
std::vector<int> v = {1, 2, 3};
return v.size();
}
static_assert(f() == 3);
Here, v's allocation is transient - the memory is deallocated when f() returns. But we can still use a std::vector during constexpr time.
As #barry explained, you cannot create variables which requires dynamic allocation and which will be still available at runtime. I believe that this is explained by the following exclusion in :
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
https://eel.is/c++draft/expr.const#5.17
a new-expression ([expr.new]), unless the selected allocation function is a replaceable global allocation function ([new.delete.single], [new.delete.array]) and the allocated storage is deallocated within the evaluation of E;
Still you can do amazing things with this new features. For example join strings:
constexpr std::string join(std::vector<std::string> vec, char delimiter) {
std::string result = std::accumulate(std::next(vec.begin()), vec.end(),
vec[0],
[&delimiter](const std::string& a, const std::string& b) {
return a + delimiter + b;
});
return result;
}
static_assert(join({ "one", "two", "three" }, ';') == "one;two;three"sv);
I have two questions. I run the below code and it sets each array object variable to 20 ın GCC 6.3 version. However, when I try the higher version of GCC, It gives an error.
Why is there this changing?
#include <iostream>
class A{
int a;
public:
A(int);
};
A::A(int init_val){
this -> a = init_val;
}
int main(){
A a[10](20);
}
Actually, I want to set an array which has more parameters for a constructor like that:
A(int, char);
A a[10](20, 'A');
The final version of the code is below.
This gives an error in every version of gcc.
Is there any way to set an array object like that?
#include <iostream>
class A{
int a;
char b;
public:
A(int, char);
};
A::A(int init_val, char init_ch ){
this -> a = init_val;
this -> b = init_ch;
}
int main(){
A a[10](20, 'A');
}
No, there is no way to initialise an array like that in C++. Neither of your attempts are well-formed.
You can initialise every element explicitly like this:
A a[10]{
{20, 'A'},
{20, 'A'},
{20, 'A'},
{20, 'A'},
{20, 'A'},
{20, 'A'},
{20, 'A'},
{20, 'A'},
{20, 'A'},
{20, 'A'},
};
If the element type were default constructible (which your class is not) and copy assignable (which your class is), then you could work around the inability by default initialising, and assigning the elements later. For example:
A a[10];
std::fill_n(a, std::size(a), A{20, 'A'});
If the initializers are actual compile time constant values, you can just add a default ctor:
class A {
...
A(): a(20) {}
}
But I know no clean way in the general case.There is a possible workaround if you can use dynamic objects: use a vector and emplace_back the objects. At the end, the vector will contain an array of objects:
va = std::vector<A>;
va.reserve(10)
for (i=0; i<10; i++) {
va.emplace_back(20, 'a');
}
A *a = va.data(); // you now have a array of 10 A objects
You simply have to make the backing vector last as long as you need the array to exist...
In C++, I can statically initialize an array, e.g.:
int a[] = { 1, 2, 3 };
Is there an easy way to initialize a dynamically-allocated array to a set of immediate values?
int *p = new int[3];
p = { 1, 2, 3 }; // syntax error
...or do I absolutely have to copy these values manually?
You can in C++0x:
int* p = new int[3] { 1, 2, 3 };
...
delete[] p;
But I like vectors better:
std::vector<int> v { 1, 2, 3 };
If you don't have a C++0x compiler, boost can help you:
#include <boost/assign/list_of.hpp>
using boost::assign::list_of;
vector<int> v = list_of(1)(2)(3);
You have to assign each element of the dynamic array explicitly (e.g. in a for or while loop)
However the syntax int *p = new int [3](); does initialize all elements to 0 (value initialization $8.5/5)
To avoid endless push_backs, I usually initialize a tr1::array and create a std::vector (or any other container std container) out of the result;
const std::tr1::array<T, 6> values = {T(1), T(2), T(3), T(4), T(5), T(6)};
std::vector <T> vec(values.begin(), values.end());
The only annoyance here is that you have to provide the number of values explicitly.
This can of course be done without using a tr1::array aswell;
const T values[] = {T(1), T(2), T(3), T(4), T(5), T(6)};
std::vector <T> vec(&values[0], &values[sizeof(values)/sizeof(values[0])]);
Althrough you dont have to provide the number of elements explicitly, I prefer the first version.
No, you cannot initialize a dynamically created array in the same way.
Most of the time you'll find yourself using dynamic allocation in situations where static initialization doesn't really make sense anyway. Such as when you have arrays containing thousands of items. So this isn't usually a big deal.
Using helper variable:
const int p_data[] = {1, 2, 3};
int* p = (int*)memcpy(new int[3], p_data, sizeof(p_data));
or, one line
int p_data[] = {1, 2, 3}, *p = (int*)memcpy(new int[3], p_data, sizeof(p_data));
Never heard of such thing possible, that would be nice to have.
Keep in mind that by initializing the array in the code that way
int a[] = { 1, 2, 3 };
..... only gains you easier code writing and NOT performance.
After all, the CPU will do the work of assigning values to the array, either way you do it.
I have a class A whose constructor takes as one of its arguments as an std::valarray[^1], however I have found that its values change to large random numbers once the constructor has finished.
As an aside, there could be an issue with my getters and setters, which I just started using in the cotext of C++ with the help of this SO question and 18.3.5 Accessor Functions from Stroustrup's The C++ Programming Language.
Stroustrup's format of constexpr double real() const { return re; } is what my example ended up using, however without constexpr since my class didn't like it (I'm trying to understand why but still haven't got it yet).
A comment on the accepted answer of my question by user deidei says that std::valarray is "dynamic", which I guess here means that you can resize it. That might bring issues when it comes to memory, and I think that my values might have been overwritten somehow due to this fact.
#include <iostream>
#include <valarray>
#include <vector>
class A
{
public:
A(std::valarray<int> b, std::vector<int> c)
{
std::cout<< b[0] << std::endl;
};
std::valarray<int> get_b() const {return b;};
std::vector<int> get_c() const {return c;};
private:
std::valarray<int> b;
std::vector<int> c;
};
int main()
{
std::valarray<int> b = {1, 3};
std::vector<int> c = {4, 2, 8, 17};
A a(b,c);
std::cout<< a.get_b()[0] << std::endl; // returns a random large number such as 26618992
return 0;
}
What issue might be causing this problem?
[^1]: (inspired by the answer to my previous question, for those of you playing along at home)
My understanding was that since the constructor takes b and c as
arguments then they will be assigned to member variables of the same
name
This is incorrect. You can initialize member variable via member initializer list, via default initialization or by assigning values directly in the body of constructor. For example:
A(std::valarray<int> b, std::vector<int> c)
: b(b), c(c)
{...
or
private:
std::valarray<int> b = {1, 3};
std::vector<int> c = {4, 2, 8, 17};
or
A(std::valarray<int> b, std::vector<int> c)
{
this->b = b;
this->c = c;
...
You have done none of the above, so your valaray of int's is size of 0. When you return its copy and trying to access non-existing element
// returns a random large number such as 26618992
you get some garbage value from the memory.