14

I was looking at std::unordered_map and saw that if I wanted to use a string as the key, I'd have to create a class containing a functor.

Out of curiosity, I was wondering if a lambda could be used in place of this.

Here's the working original:

struct hf
{
  size_t operator()(string const& key) const
  {
    return key[0];  // some bogus simplistic hash. :)
  }
}

std::unordered_map<string const, int, hf> m = {{ "a", 1 }};

Here's my attempt:

std::unordered_map<string const, int, [](string const& key) ->size_t {return key[0];}> m = {{ "a", 1 }};

That failed with the following errors:

exec.cpp: In lambda function:
exec.cpp:44:77: error: ‘key’ cannot appear in a constant-expression
exec.cpp:44:82: error: an array reference cannot appear in a constant-expression
exec.cpp: At global scope:
exec.cpp:44:86: error: template argument 3 is invalid
exec.cpp:44:90: error: invalid type in declaration before ‘=’ token
exec.cpp:44:102: error: braces around scalar initializer for type ‘int’

Given the errors, it would seem that the lamba is different enough from a functor that it makes it not a constant expression. Is that correct?

6
  • 2
    std::hash is specialized for std::string, no need to provide something yourself if you don't want to improve / change the hash. Also, think about what you're doing: std::unordered_map expects a type as the template argument, and a lambda expression is exactly that - an expression, i.e., a value, not a type. Commented May 26, 2013 at 19:59
  • I found that in the g++ compiler I was using (v4.5.3 with -std=gnu++0x), it would give me a bunch of errors if I didn't specify the hash function when using a string key. Commented May 26, 2013 at 20:13
  • As for it being an expression, yeah, I guess that would be the answer. Commented May 26, 2013 at 20:14
  • 1
    Related: stackoverflow.com/questions/15719084/… Commented May 26, 2013 at 21:36
  • 2
    4.5.3 supports very little of C++11. Don't use this combination for anything serious. Commented May 27, 2013 at 15:37

1 Answer 1

12

The way to pass the lambda function is:

auto hf = [](string const& key)->size_t { return key[0]; };

unordered_map<string const, int, decltype(hf)> m (1, hf);
                                 ^^^^^^^^^^^^        ^^
                                 passing type        object

The output of decltype(hf) is a class type which doesn't have default constructor (it's deleted by =delete). So, you need pass the object by constructor of unordered_map to let it construct the lambda object.

Sign up to request clarification or add additional context in comments.

2 Comments

@MM., like this: std::unordered_map<string const, int, decltype(hf)> m(1, hf);. The first argument is the initial bucket count, the second is the hasher. You can't use brace-initialization in this case, sadly.
Interesting, though kinda cheating as you are not just passing the lambda function as a template parameter. In fact, the template parameter can be avoided all together via a helper template function like: template<class HASHER> auto make_unordered_map(size_t bucketCount, HASHER const & hf) -> unordered_map<string const, int, decltype(hf)> { return unordered_map<string const, int, decltype(hf)>(bucketCount, hf); } as seen here. Still, it is interesting. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.