4

Tried to use C++11 lambda as a key accessor for boost::multi_index:

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/global_fun.hpp>

struct Foobar {
    int key;
};

void func()
{
    namespace mii = boost::multi_index;
    typedef boost::multi_index_container< Foobar,
            mii::hashed_unique< mii::global_fun< const Foobar &, int,
            []( const Foobar &f ) { return f.key; } > > > Container;

}

But getting compile error from g++ 4.8.2 and boost 1.53:

error: could not convert template argument '<lambda closure object>func()::__lambda0{}' to 'int (*)(const Foobar&)'

This answer Using Boost adaptors with C++11 lambdas suggests to convert into std::function which does not work in this case. Is there a simple way to fix this?

Community
  • 1
  • 1
Slava
  • 43,454
  • 1
  • 47
  • 90

1 Answers1

3

Lambdas may not be used in unevaluated contexts1. I'm not sure if this qualifies as an unevaluated context, but the approach involving decltype( [](int){} ) would2.

Stateless Lambdas do not appear to have a constexpr conversion-to-function-pointer (this is probably an oversight), otherwise this would work:

template<class T>using type=T;
template< void(*)(int) > struct test {};

constexpr type<void(int)>* f = [](int){};

int main() {
  test<f> x;
}

and it might even work if you passed the lambda directly to a void(*)(int) function-pointer parameter.

This leaves you with writing your lambda as an old-school function.


1 This is probably to make compiler's lives easier (as far as I can tell, the type of a lambda in a header file need not be consistent between compilation units under the current standard? But I'm not certain about that.)

2 This prevents you from passing it as a pure type and then invoking it. Lambdas also lack constructors. A hack whereby you construct a stateless lambda (or a reference to same) then invoke it would work in every implementation, unless the compiler noticed your slight of hand, but it is undefined behavior.

This leads to this hack:

#include <iostream>

auto f() { return [](){std::cout <<"hello world.\n";}; }

template<class Lambda>
struct test {
  void operator()() const {
    (*(Lambda*)nullptr)();
  }
};

int main() {
  test<decltype(f())> foo;
  foo();
}

which is both useless and undefined behavior, but does invoke a lambda we passed as a template parameter to test technically. (C++14)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thanks for the answer. Is there in your opinion way to fix it in boost (creating special key extractor for example) or it requires language change? – Slava Sep 11 '14 at 17:13
  • @Slava language as far as I can tell: make stateless lambda to function pointer `constexpr` and we might be good. – Yakk - Adam Nevraumont Sep 11 '14 at 17:35