2d vector default value in class header

Greetings,

I would like to give a class member default values within the header file of the class, so that I don't have to write a .cpp file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// .h file
class measurement{


    public:

        void take_measurement(std:: vector <double> parameters, std:: vector <double> velocities){  
            
            measured_values[0].push_back(parameters[0]);
            measured_values[1].push_back( 0.5* (velocities[0]*velocities[0] + velocities[1]*velocities[1]) );
        
        }

        std:: vector < std::vector <double> > measured_values(2);  // gives error
        int testmember = 1;  // is allowed
};



I get a compilation error complaining about the 2 in the vector declaration:
error: expected identifier before numeric constant

I want to have a vector of two empty vectors. What's the problem here?

Regards!
A default member initializer should be a brace-or-equals-initializer.

1
2
3
4
5
class measurement{
    // ...
    std:: vector < std::vector <double> > measured_values {2} ; // note: {}
    int testmember = 1;  
};

There is a pitfall - you can't just blindly use braces everywhere. The problem is that vector v below only has one element with the value 2:
std::vector<int> v{2}
While vector u has 2 elements with value 0:
std::vector<int> u(2)

(The pitfall doesn't apply to your case.)

If you need to use the parentheses but aren't allowed, you can say
class a { std::vector<int> v = std::vector<int>(2); };
Or do the initialization in a member initializer list:

1
2
3
4
5
6
7
8
struct measurement
{
  std:: vector < std::vector <double> > measured_values;

  explicit measurement()
    : measured_values(2)
  {}
};

Last edited on
If you need to use parentheses instead of braces, then:

 
std::vector < std::vector <double> > measured_values(2, std::vector<double>(0));

Last edited on
> If you need to use parentheses instead of braces, then: ...

Repeat: A default member initializer should be a brace-or-equals-initializer.
A default member initializer should be a brace-or-equals-initializer.


Yes - if the C++ version used supports this.


@PhysicsIsFun - which C++ version/compiler are you using?

 
std::vector < std::vector <double> > measured_values(2);


Compiles OK as C++latest with VS2022.
Last edited on
Compiles OK as C++latest with VS2022.

Is that a standard feature or a VS extension?
If former, where in the standard that is described?
Sometimes C++'s logic defies belief!

1
2
3
4
5
6
7
8
9
10
11
#include <vector>

class C
{
   std::vector < std::vector <double> > measured_values(2);          // not OK!
};

int main()
{
   std::vector < std::vector <double> > measured_values(2);          // OK!
}
Last edited on
Yeah - I haven't yet recovered from a couple of nights ago (never again during the week... - I currently make a good impression of 'Night Of The Living Dead'). I saw mbozzi's post and interpreted it wrongly (as a language version issue) rather than a syntax issue. I've altered my previous posts with a strike-through!

The problem is that a class member variable direct initialization with params won’t work. It is tried to be treated as a function declaration (the most vexing parse issue) - hence the error.

In a class either use {} syntax (as repeatedly correctly stated by Borges) or as mbozzi's post adjusted for a 2d-vector (using equals initializer - again as correctly stated by Borges above):

 
std::vector < std::vector <double> > measured_values = std::vector<std::vector<double>>(2);


PS. Hopefully by after Boxing Day, I'll have recovered sufficiently to post coherently!
Last edited on
Sometimes C++'s logic defies belief!

You do know that C++11 did add brace initialization (to skim around the most vexing parse) and the default member initialization.

Hence, before C++11 there was no default member initialization at all and adding anything that could fall to most vexing parse definitely was not the objective of C++11, I reckon.
keskiverto wrote:
You do know that C++11 did add brace initialization ...

It was possible to initialize structs and arrays using the ={...} syntax before C++11.

C++11 made the = optional, made it usable for more types and banned narrowing conversions.
Last edited on
But why not just fix this issue with member definition vs non-member definition?
You mean why not allow parentheses to specify default member initializers?

I think the answer has already been given by keskiverto. They wanted to avoid the "most vexing parse" problem. That's the reason I have also heard.

I think there might also been an overexcitement about the new "uniform initialization" at the time, and a lack of understanding that we still need/want to use these other initialization syntaxes as often as we still do.
Last edited on
I looked at some old papers and it seems like the original proposal was to allow all kinds of initialization syntaxes.

N1959 wrote:
The basic idea is to allow non-static attributes of class and struct types to be initialized where declared. All of the same initialization syntaxes may be used as for initialization of local variables.
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1959.pdf


In a later version of the proposal the wording had changed:

N2628 wrote:
The basic idea is to allow non-static data members of class types to be initialized where declared. The goal is that all of the same initialization syntaxes may be used as for initialization of local variables; [...] This goal will not be reached completely, however, because to avoid parsing problems, we no longer propose to allow initializers of the “( expression-list )” form as explained below. On the other hand, there now seems to be a consensus on the use of {} for initializers (N2575), so we use that form for some examples that used parentheses in earlier versions of this paper.
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2628.html

For the main problem they're referring to, see An issue raised in Kona regarding scope of identifiers » Problem 1 later in the same paper.
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2628.html#konaissue

It is related to the "most vexing parse" but the problem seem to be more confusing because you can refer to members that are declared later.
Last edited on
Thank you, guys, especially Borges,

using
1
2
3
4
class measurement{
    // ...
    std:: vector < std::vector <double> > measured_values {2} ; // note: {}
};

worked like a charm.
I will remember that statement:
"A default member initializer should be a brace-or-equals-initializer."
Not sure why he said "The pitfall doesn't apply to your case." I think it does.


you can't use (2) for a member variable. Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <vector>
#include <iostream>

struct Z {
	std::vector < std::vector <double> > vv1 { 2 };
	std::vector < std::vector <double> > vv2 = std::vector<std::vector<double>>(2);
};

int main() {
	Z z;

	std::cout << "size of vv1: " << z.vv1.size() << '\n';
	for (size_t c {}; const auto & y : z.vv1)
		std::cout << c++ << " has " << y.size() << " elements\n";

	std::cout << "\nsize of vv2: " << z.vv2.size() << '\n';
	for (size_t c {}; const auto & y : z.vv2)
		std::cout << c++ << " has " << y.size() << " elements\n";
}


which displays:


size of vv1: 2
0 has 0 elements
1 has 0 elements

size of vv2: 2
0 has 0 elements
1 has 0 elements


Both vv1 and vv2 have 2 vectors each with no elements.

PS. And even with non-member definition using (2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <vector>
#include <iostream>

int main() {
	std::vector < std::vector <double> > vv1 { 2 };
	std::vector < std::vector <double> > vv2(2);

	std::cout << "size of vv1: " << vv1.size() << '\n';
	for (size_t c {}; const auto & y : vv1)
		std::cout << c++ << " has " << y.size() << " elements\n";

	std::cout << "\nsize of vv2: " << vv2.size() << '\n';
	for (size_t c {}; const auto & y : vv2)
		std::cout << c++ << " has " << y.size() << " elements\n";
}


this still displays:


size of vv1: 2
0 has 0 elements
1 has 0 elements

size of vv2: 2
0 has 0 elements
1 has 0 elements


as required.

And if some elements are added to the outer vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <vector>
#include <iostream>

using Matrix = std::vector < std::vector <double> >;

void display(const Matrix& m) {
	std::cout << m.size() << '\n';
	for (size_t c {}; const auto & y : m)
		std::cout << c++ << " has " << y.size() << " elements\n";

	std::cout << '\n';
}

int main() {
	Matrix vv1 { 2 };
	Matrix vv2(2);

	std::cout << "size of vv1: ";
	display(vv1);

	std::cout << "size of vv2: ";
	display(vv2);

	vv1[0].push_back(2);
	vv2[1].push_back(3);
	vv2[1].push_back(4);

	std::cout << "After adding elements\nsize of vv1: ";
	display(vv1);

	std::cout << "size of vv2: ";
	display(vv2);

	std::cout << vv1[0][0] << '\n';
	std::cout << vv2[1][1] << '\n';
}


then it displays as expected:


size of vv1: 2
0 has 0 elements
1 has 0 elements

size of vv2: 2
0 has 0 elements
1 has 0 elements

After adding elements
size of vv1: 2
0 has 1 elements
1 has 0 elements

size of vv2: 2
0 has 0 elements
1 has 2 elements

2
4

Last edited on
@seeplus
Yeah, I realized my mistake and removed my post before you posted. I'm sorry about that.
Last edited on
Topic archived. No new replies allowed.