2

I've got a class with constructors defined like this:

LambdaJSONVisitor();
LambdaJSONVisitor(boost::function<void (const Value &)> f);
LambdaJSONVisitor(boost::function<void (const Object &)> f);
LambdaJSONVisitor(boost::function<void (const KeyValuePair &)> f);
LambdaJSONVisitor(boost::function<void (const Array &)> f);

and I'm trying to construct an object like this:

LambdaJSONVisitor setNodeIDVisitor([&](const JSONAPI::Value &val) -> void
{
    ...
});

When I try to compile it, I'm getting the following compiler error:

4>netmodel\CNetworkAlarmBuilder.cpp(60): error C2668: 'JSONAPI::LambdaJSONVisitor::LambdaJSONVisitor' : ambiguous call to overloaded function
4>          C:\workspace\client\projects\JSONParser\API/LambdaJSONVisitor.h(21): could be 'JSONAPI::LambdaJSONVisitor::LambdaJSONVisitor(boost::function<Signature>)'
4>          with
4>          [
4>              Signature=void (const JSONAPI::Array &)
4>          ]
4>          C:\workspace\client\projects\JSONParser\API/LambdaJSONVisitor.h(20): or       'JSONAPI::LambdaJSONVisitor::LambdaJSONVisitor(boost::function<Signature>)'
4>          with
4>          [
4>              Signature=void (const JSONAPI::KeyValuePair &)
4>          ]
4>          C:\workspace\client\projects\JSONParser\API/LambdaJSONVisitor.h(19): or       'JSONAPI::LambdaJSONVisitor::LambdaJSONVisitor(boost::function<Signature>)'
4>          with
4>          [
4>              Signature=void (const JSONAPI::Object &)
4>          ]
4>          C:\workspace\client\projects\JSONParser\API/LambdaJSONVisitor.h(18): or       'JSONAPI::LambdaJSONVisitor::LambdaJSONVisitor(boost::function<Signature>)'
4>          with
4>          [
4>              Signature=void (const JSONAPI::Value &)
4>          ]
4>          while trying to match the argument list '(`anonymous-namespace'::<lambda1>)'

is it possible to pass a lambda as a parameter to an overridden constructor like this? If so, what am I doing wrong and how should I change the code to make it work? I'm using Visual Studio 2010.

Thanks

JS.
  • 616
  • 4
  • 13
  • Simple example works fine http://liveworkspace.org/code/9YSdj$5 – ForEveR Apr 05 '13 at 09:37
  • Yes I wonder if this is a VS2010 specific issue, just out of interest I tried changing the constructor signatures to take const refs to functions to match your example, but I get the same error. – JS. Apr 05 '13 at 09:43

2 Answers2

3

It can't work only when there are derived types Example this can be fixed by creating object and casting it to right type, something like

auto lambda = [&](const C&) {};
Func f(static_cast<std::function<void(const C&)>>(lambda));

or without creating object like

Func f(static_cast<std::function<void(const C&)>>(
[&](const C&) {});

Example

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • depending on the frequency you have to create visitors from lambdas, consider the "First step" in my answer - it makes the call shorter. – Arne Mertz Apr 05 '13 at 10:23
2

With some glue code, you can let the compiler find out what the lambda's argument type is and call the corresponding constructor, without an explicit cast. However, the only thing you will need is at least a move constructor for your Visitor.

First step: make a "templatized" constructor. I suppose, LambdaJSONVisitor cannot be changed, so you'll need a helper function:

template <class Arg>
LambdaJSONVisitor createLJV_Arg(std::function<void(const Arg&)> f)
{ return LambdaJSONVisitor(f); }

You can now call that function by providing the template parameter explicitly:

LambdaJSONVisitor v = createLJV_Arg<Value>( [](Value const&){} );
                                 // ^-- explicitly state the type

Second step: make a template metaprogramming function that determines the argument type of your lambda and passes it explicitly to the first function. Example can be found here:

template <class F>
LambdaJSONVisitor createLJV(F&& f)
{
   typedef boost::function_types::parameter_types<decltype(&F::operator())>::type args_t;
   typedef boost::mpl::at<args_t, boost::mpl::int_<1>>::type arg1_t;

   return createLJV_Arg<arg1_t>(std::forward<F>(f));
}

And then just

LambdaJSONVisitor v = createLJV( [](Value const&){} );

Of course, if you can modify LambdaJSONVisitor, then give it just one templated constructor and do the lambda's argument type deduction inside that.

Community
  • 1
  • 1
Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • Thanks, I didn't consider metaprogramming. That's a nice clean solution, and since the LJV is created in lots of places, I think it's definitely worth the additional complexity in the constructor (I can modify LJV so I'll do it that way). – JS. Apr 05 '13 at 12:25