Beware of C++11 `auto` | Code Trips & Tips on WordPress.com

Marco Massenzio
3 min readDec 10, 2017
Image credit: Stefan Keller, https://pixabay.com/en/users/kellepics-4893063/

Here’s a puzzler for you: given the code below, what does the for loop print?

using HandlersMap = std::map<std::string,
std::function<std::string(const std::string&)>>;
using MetaMap = std::map<std::string, HandlersMap>;

void insert(MetaMap& meta,
const std::string& key,
const std::string& sub) {
auto handler = meta[key];
handler[sub] = [=](const std::string& msg) {
return "The response for " + sub + " is: " + msg;
};
}

int main(int argc, char **argv) {
MetaMap meta;

insert(meta, "get", "/api/v1/test1");
insert(meta, "get", "/api/v1/test2");
insert(meta, "post", "/api/v1/test3");

for (auto m : meta) {
cout << m.first << ":" << endl;
for (auto h : m.second)
cout << "\t" << h.first << " --> " << h.second("message") << endl;
}
}

You would be forgiven for assuming that this would be the output:

get:
/api/v1/test1 --> The response for /api/v1/test1 is: message
/api/v1/test2 --> The response for /api/v1/test2 is: message
post:
/api/v1/test3 --> The response for /api/v1/test3 is: message

what you get instead is a rather disappointing:

get:
post:

the difference being a tiny weeny &: in order to get the desired output, you would have to change this line:

// Note the change to auto&
auto& handlers = meta[key];

Or, even better, use this:

meta[key][sub] = [=](const std::string& msg) {
return "The response for " + sub + " is: " + msg;
};

This is even the more confusing, by looking at the signature of operator[] in the stl::map header:

// In stl_map.h 
mapped_type&
operator[](const key_type& __k)

which states rather unequivocally that you will be getting a reference to whatever value was stored, in relation to the key __k (or, a reference to a newly-created, and thence stored, instance of mapped_type: this being the root of the requirement, for value types in STL maps, to have a default constructor).

This was driving me crazy while developing a “mapped routes” handling mechanism for an API Server I am designing (more on this in another blog post) until I figured out that the “nested” invocation (meta[key][sub]) was working, while the “split” call was not.

As specified in the Rules for auto resolution, when used for a variable initializer, the compiler will “[use] the rules for template argument deduction from a function call”; in this case, the absence of any modifier, leads the compiler to detect the type of handler as U as if an imaginary function f were defined as:

template<typename U> void f(U expr); // Now invoke f() to determine the type to replace `auto` with.
f(meta[key]);

thus implicitly converting the reference type of the returned value, into an actual class type (std::map<K, V>).

This has the rather undesired side-effect of turning the assignment into a copy constructor of the map stored as the value, handlers: a local variable, which will be destructed upon exiting from the function, and any changes thereof to be irretrievably lost.

However, using auto&, we convert the above into:

template<typename U> void f(U& expr);

thus yielding the expected reference type and, ultimately, the desired outcome.

Originally published at codetrips.com on December 10, 2017.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response