Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you initialize a map which takes a struct as value?

Tags:

I am using a map as an associative array of IDs -> value, where the value is a struct defining the object:

#include <map>  struct category {         int id;         std::string name; };  std::map<int, category> categories;  int main() {         categories[1] = {1, "First category"};         categories[2] = {2, "Second category"};  } 

The above code compiles with g++, but with the following warning:

warning: extended initializer lists only available with -std=c++0x or -std=gnu++0x 

I have read various questions/answers here about struct initialization, but I'm still a bit confused. I have a series of related questions:

  1. I could add the compiler option -std=c++0x and be done with the warning, but still be none the wiser about the underlying problem. Wouldn't things break if I add a method to the category struct?

  2. What would the best way be to initialize this POD struct (category) in a more C++03 compliant way?

  3. Basically, I am not yet sure of the consequences of doing things one way rather than another way. This kind of associative array (where the key is the ID of an object) is easy with PHP, and I'm still learning about the proper way to do it in C++. Is there anything I should pay attention to in the context of the code above?

Edit
The following questions are related, but I didn't understand the answers when I first read them:
C++ initialize anonymous struct
c++ Initializing a struct with an array as a member
Initializing structs in C++

like image 472
augustin Avatar asked Dec 09 '10 09:12

augustin


People also ask

Can you initialize struct values?

Structure members cannot be initialized with declaration.

How do you initialize a structure object?

When initializing an object of struct or union type, the initializer must be a non-empty, (until C23) brace-enclosed, comma-separated list of initializers for the members: = { expression , ... }

How do you declare a struct in C++?

How to declare a structure in C++ programming? The struct keyword defines a structure type followed by an identifier (name of the structure). Then inside the curly braces, you can declare one or more members (declare variables inside curly braces) of that structure.


2 Answers

In C++ (ISO/IEC 14882:2003), a brace enclosed list of expressions can be used to initialize a variable of aggregate type in the declaration that defines it.

E.g.

struct S { int a; std::string b; };  S x = { 39, "Hello, World\n" }; 

An aggregate type is an array or a class with no user-declared constructors, no private or protected non-static data members, no base classes, and no virtual functions. Note that a class aggregate doesn't have to be a POD-class and any array is an aggregate whether or not the type that it is an array of is an aggregate.

However, a brace-enclosed list of expressions is only valid as an initializer for an aggregate, it is not generally allowed in other contexts such as assignment or a class constructor's member initialization list.

In the current draft of the next version of C++ (C++0x), a brace enclosed list of expressions (brace-init-list) is allowed in more contexts and when an object is initialized from such an initializer list it is called list-initialization.

New contexts where such a list is allowed include arguments in a function call, function returns, arguments to constructors, member and base initializers and on the right hand side of an assignment.

This means that this is not valid in C++03.

int main() {         categories[1] = {1, "First category"};         categories[2] = {2, "Second category"}; } 

Instead you could do something like this.

int main() {         category tmp1 = { 1, "First category" };         category tmp2 = { 2, "Second category" };          categories[1] = tmp1;         categories[2] = tmp2; } 

Alternatively.

int main() {         category tmpinit[] = { { 1, "First category" },                                { 2, "Second category" } };         categories[1] = tmpinit[0];         categories[2] = tmpinit[1]; } 

Or, you could consider making a factory function for your type. (You could add a constructor for your type but this would make your class a non-aggregate and would prevent you from using aggregate initialization in other places.)

category MakeCategory( int n, const char* s ) {     category c = { n, s };     return c; }  int main() {     categories[1] = MakeCategory( 1, "First category" );     categories[2] = MakeCategory( 2, "Second category" ); } 
like image 82
CB Bailey Avatar answered Oct 23 '22 20:10

CB Bailey


In the current C++ standard, you can use initializer lists to initialize arrays and structs containing POD values only. The next standard (aka C++0x or C++1x) will allow to do the same on structs containing non-POD types, e.g. std::string. That's what the warning is about.

I'd suggest you add a simple constructor to category that takes the id and name and simply call that constructor instead:

#include <map> #include <string>  struct category {         category() : id(0), name() {}         category(int newId, std::string newName)          : id(newId), name(newName) {}          int id;         std::string name; };  std::map<int, category> categories;  int main() {         categories[1] = category(1, "First category");         categories[2] = category(2, "Second category");  } 
like image 24
Mephane Avatar answered Oct 23 '22 19:10

Mephane