views:

343

answers:

3

Hi, I get the following error with the code below.

expected constructor, destructor, or type conversion before '=' token

--

#include <string>
#include <map>

class Foo {

};

std::map<std::string, Foo> map;
map["bar"] = Foo();

int main()
{

    return 0;
}
+11  A: 
map["bar"] = Foo(); // This line is a statement not a declaration.
                    // You have to put it in main, or any execution context


Untill C++0x becomes mainstream, I would suggest using boost. Filling the map becomes piece of cake. Here is an example:

std::map<std::string, Foo> mymap;
...
int main()
{
  insert(mymap)
   ("First",  Foo(...))
   ("Second", Foo(...))
   ("Third",  Foo(...));
   ...
}
AraK
A: 

It looks like what you want is a static initializer. I suggest you read this. Its illustrative of the use of static initializers and also of their primary pitfall, static initialization order.

Jherico
Why the downvotes? At least leave a constructive comment.
Tim Sylvester
I didn't downvote you, but I think I see why yours isn't a good answer. It doesn't address the question. Static initialization and the ordering dangers may be part of an answer to his question but you didn't include that.
Zan Lynx
There is no such thing as a static initializer in c++. Globals are initialized by there constructors. Also the linked article is terrible and full of really bad advice.
Martin York
Well, far be it from me to contradict someone with such a high rating (and a grammatical error in *their* comment), but I call bullshit. The C++ faqs is a respected site which has spawned a respected book. No, technically there isn't a 'static initializer' in C++ but there is a 'construct on first use' idiom which accomplishes the same thing, and if the OP is trying to initialize a global map with values, then this idiom is likely to be useful to him.
Jherico
+2  A: 

Short answer, as you've seen, is: you can't do that.

I think what you really want is this:

std::map<std::string, Foo> map;

int main()
{
    map["bar"] = Foo();

If you really need the initialization to execute before main() you will often see examples like this:

namespace {
   struct StaticInitHelper {
       StaticInitHelper() { map["bar"] = Foo(); }
   } _helper_obj;
}

However, now you have a new problem which is that there's no guarantee that map is created before _helper_obj. One way around this is to combine them:

namespace {
   struct StaticInitHelper : public std::map<std::string, Foo> {
       StaticInitHelper() { (*this)["bar"] = Foo(); }
   } map;
}

Inheriting from STL container classes is generally not recommended, however. Note that this example hides any other constructors and the STL base class does not have a virtual destructor. This would be considered a "hack" by many, and should really be avoided.

Yet another alternative is to define the class with a std::map:

namespace {
   struct StaticInitHelper {
       StaticInitHelper() { map["bar"] = Foo(); }
       std::map<std::string, Foo> map;
   } map_holder;
}

map_holder.map.find(...

But of course this complicates any use of the map.

Update:

I forgot to mention another option, using boost::assign:

#include <boost/assign/list_of.hpp>

map<int,int> map = boost::assign::map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);

I can't find information on whether that's safe on a static object, though.

Tim Sylvester