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?

Gan
  • 164
  • 1
  • 4
  • 13
Adrian
  • 10,246
  • 4
  • 44
  • 110
  • 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. – Xeo May 26 '13 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. – Adrian May 26 '13 at 20:13
  • As for it being an expression, yeah, I guess that would be the answer. – Adrian May 26 '13 at 20:14
  • Actually, hold on. Although it's an 'expression' is it not more like a function? – Adrian May 26 '13 at 20:30
  • 1
    Related: http://stackoverflow.com/questions/15719084/how-to-use-lambda-function-as-hash-function-in-unordered-map/15719698#15719698 – zch May 26 '13 at 21:36
  • 2
    4.5.3 supports very little of C++11. Don't use this combination for anything serious. – n. m. could be an AI May 27 '13 at 15:37

1 Answers1

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.

masoud
  • 55,379
  • 16
  • 141
  • 208
  • 1
    @MM., like this: `std::unordered_map 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. – avakar May 29 '13 at 18:51
  • 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 auto make_unordered_map(size_t bucketCount, HASHER const & hf) -> unordered_map { return unordered_map(bucketCount, hf); }` as seen [here](http://ideone.com/62heUn). Still, it is interesting. :) – Adrian May 30 '13 at 20:57