tags:

views:

142

answers:

6

Could you have:

template <class T>
const T &operator[] (unsigned int x)

My thinking was if you have a map<string,string> it would be nice to have a wrapper class which lets you do:

obj["IntVal"]="12";
obj["StringVal"]="Test";

int i = obj["IntVal"];

How close to this can we actually get in C++? Is it worth the pain?

+5  A: 

You can't - in:

int i = obj["IntVal"]; 

the actual type of T can't be inferred from the context since the return type isn't part of the function signature.

Moreover, storing integer values as strings is not considered as best practices, due to memory and performance considerations ;-)

Seb
@Seb A good answer, which I hope I have improved with my edit. If not, feel free to roll it back.
anon
You did :-) As a newcomer, I didn't know you could use rich text formatting, sorry
Seb
@Seb No need to apologise - to format code, select it and then use the 1010 button above the text entry area. Don't try to use HTML tags.
anon
Nice, concise answer. +1
tstenner
"storing integer values as strings is not considered as best practices"... well of course, but it still happens in many cases, any time XML or text-mode files are used for instance.
John
A: 

Have you looked at boost variant? Is this what you're looking for?

Amit Kumar
A: 

Well, what you wrote in your sample code doesn't match the question. Right now, you only have the return type templated.

But if you wanted to do something like:

template <class T>
const T &operator[const T& x]

that's valid, though maybe not terribly useful.

miked
+2  A: 

Not worth it.

Templating the return type means you'd have to explicitly specify the template parameter when you call it. Something like this, maybe I have the syntax wrong:

int i = obj.operator[]<int>("IntVal");

C++ does not deduce template parameters from what you assign the result of the call to, only from the parameters you call the function with.

So you might as well just define a normal function:

int i = obj.get<int>("IntVal");

Or in this case, either do this or implement get using this:

int i = boost:lexical_cast<int>(obj["IntVal"]);

As Amit says, you could define operator[] to return a type which can be converted either to int or to other types. Then your example code can be made to compile without the explicit lexical_cast.

Steve Jessop
A: 

A map already provides an overloaded operator[] that does most of what you want. The thing you seem to want that's missing is implicit conversion from a string that happens to contain digits to an integer. One of the fundamental characteristics of C++ is static typing, which says that shouldn't be allowed -- so it's not. It'll be happy to do that conversion if you want, but you'll have to ask for it:

int i = lexical_cast<int>(obj["IntVal"]);

Alternatively, you could create a string-like class that supported implicit conversion to int. Personally, I'd advise against that. I don't object to implicit conversions nearly as strongly as many people do, but that still strikes me as a pretty lousy idea, at least for most general use.

Jerry Coffin
+4  A: 

You can also do

class Class {
  struct Proxy {
    template<typename T> T as() { ... }
    template<typename T> operator T() { return as<T>(); }
  private:
    Proxy(...) { ... }
    Proxy(Proxy const&); // noncopyable
    Proxy &operator=(Proxy const&);
    friend class Class;
  };

public:
  Proxy operator[](std::string const& s) { ... }
};

Class a;
int i = a["foo"];
int i = a["foo"].as<int>();

T will be deduced to whatever the to be initialized object is. And you are not allowed to copy the proxy. That said, i prefer an explicit as<T> function like another one proposed too.

Johannes Schaub - litb