Slava
Slava

Reputation: 44258

Using C++11 lambda with boost::multi_index

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?

Upvotes: 4

Views: 683

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275405

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)

Upvotes: 3

Related Questions