0

I am writing a program that passes around a struct type that I don't want to be modified. This struct has two const members, and looks as follows:

struct system_s {
  std::string name;
  std::string pkg;
  char *const start_cmd[10];
  char *const end_cmd[10];
  bool ros;

  bool equals(const system_s &cmp);
};

The struct is being stored in a map with the following format. It is a class member:

std::map<std::string, system_s> sys_map;

There is another temporary map. Think of sys_map as a cache if you prefer. But really you don't have to worry about how it is being used for the sake of this question. sys_map is being called to add a system to the temporary map as follows. It is in a class method:

add_system(sys_map[msg->system]); (*)

This function has the following definition. It is a class method:

int add_system(const system_s &sys);

When (*) is called, I get the following error:

system.h: In instantiation of ?std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = system_s; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, system_s> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = system_s; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = std::basic_string<char>]?:
/tc_manager_node.cpp:74:41:   required from here
/system.h:26:8: error: uninitialized member ?system_s::start_cmd? with ?const? type ?char* const [10]? [-fpermissive]
 struct system_s {
        ^
system.h:26:8: error: uninitialized member ?system_s::end_cmd? with ?const? type ?char* const [10]? [-fpermissive]
In file included from /usr/include/c++/4.8/map:61:0,
                 from /opt/ros/indigo/include/ros/console.h:42,
                 from /opt/ros/indigo/include/ros/ros.h:40,
                 from 

/tc_manager_node.cpp:2: /usr/include/c++/4.8/bits/stl_map.h:469:59: note: synthesized method ?system_s::system_s()? first required here __i = insert(__i, value_type(__k, mapped_type()));

Why is this member of type system_s 'uninitialized'? It presumably stored already initialized in sys_map. Does it have something to do with passing the system_s as a reference in int add_system(const system_s &sys)?

errolflynn
  • 641
  • 2
  • 11
  • 24

3 Answers3

1

As @Greg Kikola said, const members must be initialized. Check here on how to do that with initializer lists (Not to be confused with std::initializer_list): http://en.cppreference.com/w/cpp/language/initializer_list

Gambit
  • 954
  • 9
  • 15
  • My assumption was that this would only be a problem if I actually attempted to initialize the variable without initializing the `const pointer`. Nowhere in that code should be calling the constructor to my knowledge, since the `struct` variable is passed as a reference. That being said, I'm clearly wrong somehow. The problem that I am having is casting to a `char *const *` type. Which type can be passed to a constructor to do so? – errolflynn Feb 13 '17 at 00:41
  • You have to be constructing it some where even to pass it as a reference. – Gambit Feb 13 '17 at 03:00
  • See @M.M's answer – errolflynn Feb 13 '17 at 06:27
1

The position of the const with pointers can sometimes be confusing. X * const p indicates:

“p is a const pointer to an X that is non-const”: you can’t change the pointer p itself, but you can change the X object via p. [source]

This means that the address a system_s is created with can never be changed. Which is bad since you're not constructor-initializing start_cmd or end_cmd this means that none of the 10 pointers can be assigned a valid address. They start with an uninitialized addresses and can never be assigned anything else.

EDIT: This post is tagged: . There is no straight forward way to initialize arrays in C++03. You can look at this question for some workarounds: Initializing a member array in constructor initializer If you have the ability to go with you can use List Initialization.

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Will I be able to use a constructor to initialize `start_cmd` and `end_cmd`? If so, which type in a constructor is appropriate to perform initialization (preferably in an initialization list)? These are commands that will be passed to `system()` or `exec()` calls. – errolflynn Feb 13 '17 at 00:38
  • 1
    @errolflynn I've edited. But there is no straight forward way to initialize arrays in the constructor intialization list before [tag:c++11] I have included a link with some workarounds. The easiest thing would certainly be to make `start_cmd` and `end_cmd` non-const. – Jonathan Mee Feb 13 '17 at 02:30
  • Thank you. It would probably be easier, but I think that I can use this method given that I have already begun to use them as such. The `start_cmd` and `end_cmd` variables are liable to be used for `exec` calls, which is the reason for their typing. – errolflynn Feb 13 '17 at 06:29
1

The operator[] of map (which you invoke with sys_map[msg->system]) has the possibility of creating a new entry if the map entry is not found. The new entry is default-constructed, but your class is not default-constructible.

To fix this, don't use [] on the map. Instead use find to find the entry you are looking for.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Wow, that worked! Changed it to `sys_map.find(msg->system)->second`. Will have to fix this inefficiency later, but it works in a pinch. Thank you friendo – errolflynn Feb 13 '17 at 05:00
  • 1
    @errolflynn bear in mind that this will fail miserably if the item is not found; the usual idiom would be `auto item = sys_map.find(bla); if ( item != sys_map.end() ) { ...do something with item->second... }` – M.M Feb 13 '17 at 06:14
  • Certain parts of this program are only supported within the framework of c++03. Not sure how. If I am correct, the `auto` keyword has only to do with the duration of variable storage. But yes, I can certainly use the std::map::iterator item type to do that. – errolflynn Feb 13 '17 at 06:26