By jalf


2010-02-25 16:13:32 8 Comments

I'm trying to use C++0x, and in particular lambda expression and decltype to simplify some of my code, using the MSVC10 RC compiler.

I've run into the following very odd problem:

template <typename F>
auto foo(F f) -> decltype(f()){
  return f();
}

template <typename F>
void bar(F f){
  f();
}

int main() {
  bar([](){
    foo([]() { }); // error C2893: Failed to specialize function template ''unknown-type' foo(F)'
  });
}

As indicated in the comment, the compiler generates an error on the line foo([]() { }).

I hate to shout "compiler bug", but I really can't see any good explanation for this error. Apparently, while inside the outer lambda expression, the compiler can not specialize the foo function template for the inner lambda.

However, if the definition of foo is changed to hardcode the return type, like this:

template <typename F>
void foo(F f){
  return f();
}

then everything compiles just fine.

Is there some obscure quirk of decltype when used to deduce the return type of lambda expression parameters inside the scope of another lambda that I'm not aware of?

2 comments

@GManNickG 2010-02-25 20:30:47

These are just some test cases for people to observe.

Works

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

void dummy() {}

int main()
{
    auto x = []()
            {   // non-lambda parameter
                foo(dummy);
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto f = [](){};
    auto x = [&]()
            {    // pre-defined lambda
                foo(f);
            };
}

Fails

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-argument lambda
                foo([]{});
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-scope lambda
                auto f = []{};
                foo(f);
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-scope lambda, explicit return
                // (explicit return type fails too, `-> void`)
                auto f = [](){ return; };
                foo(f);
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-argument lambda, explicit return non-void
                // (explicit return type fails too, `-> int`)
                foo([]{ return 5; }); 
            };
}

So it does seem to have to do with scope and the void type of the internal lambda, even when made explicit.(?)

@jalf 2010-02-25 21:14:29

Interesting. Looks like you've run into some very similar issues. Definitely looks like a compiler bug

@GManNickG 2010-02-25 21:47:04

@jalf: Ya, hopefully they fix it too.

@David Rodríguez - dribeas 2010-02-26 00:08:32

I have added a working example: the problems seems to arise only when the return type of the argument to foo is void

@Johannes Schaub - litb 2010-02-26 14:56:13

@David, I wonder whether it works if we change the inner lambda to []() -> void { } or []() { return void(); } ?

@David Rodríguez - dribeas 2010-02-26 15:32:40

I tested with []()->void {} and it did not compile. I don't have VS2010 at hand, so I cannot test [](){ return void(); } (can you actually do that in a return statement?)

@Johannes Schaub - litb 2010-02-26 16:41:54

@David while it's illegal in C, it's legal in C++. It comes handy in generic programming where you may not know the return type (think of boost::function<void()>). Any void expression will do. For instance return throw 4; (a throw expression has type void) or return delete new int; or return void(); or return function_returning_void();

@GManNickG 2010-02-26 19:37:40

@David: I'm trying to get your integer code to compile, but I get the same error with that as well. Are you sure it works? I'm in RC, and I tried the code above (which failed as the more verbose bar variant as well), and get the same error as the void variants. So I'm moved the int to fail, but apologies if I'm mistaken. :)

@GManNickG 2010-02-26 19:38:44

@litb, @David: Also, I tested both variants of the explicit void return, both fail. Same with that int version I can't get working :X

@Dewfy 2010-02-25 16:43:53

The nature of 'auto' is allow compiler to calculate the type. But your first example is contains recursive references on each other, so to calculate auto's of foo you need the bar and to create instance of bar you need the foo.

On other hand second example explicitly tells compiler: "It should be a pointer to function, so calm down for a time". Since pointer to function is well calculated type compiler knows what exactly will be reserved. Just for analogue: compare member's forward declaration

struct A; //forward
...
A a1; //this is an error
A *a2; //this is correct since pointer calculated in bytes

@jalf 2010-02-25 16:49:18

Hmm, foo doesn't in any way depend on bar as far as I can figure out. foo is just a dummy function which evaluates the functor it's passed as its parameter, and returns the result of that. And it is called with a small no-op lambda as its argument. Nothing to do with bar at all. Am I missing something really obvious here?

@Dewfy 2010-02-25 16:53:48

@jalf - specifying template function and using it implicitly you enforces compiler to calculate type. Just try to change implicit to explicit: foo<Some-explicit-Type>([]() { });

@jalf 2010-02-25 16:56:36

But the template argument for foo is just the unnamed lambda (which I can't very well specify explicitly). It should be able to deduce that type without referring to bar at all

@Dewfy 2010-02-25 17:00:53

@jalf - YES! I'm exactly talking about it. unnamed lambda from your example creates recursive reference - compiler cannot deduce it. So break it but some explicit declaration

@jalf 2010-02-25 17:01:38

A recursive reference to what?

@Dewfy 2010-02-25 17:07:28

(1) bar accepts unnamed lambda, trying deduce it type by resolving argument (2) foo uses template argument F that is implicit, so let's try to be deduced from bar, (1) ....

@jalf 2010-02-25 17:09:47

foo uses template argument F which is implicit, but why should it be deduced from bar? I'm still not seeing it. Remember they're two different lambda expressions. The lambda passed to bar has nothing to do with the lambda passed to foo.

@Dewfy 2010-02-25 17:15:55

"foo uses template argument F which is implicit, but why should it be deduced from bar" - but how complier can deduce this info? so you have got an error above. I don't talk that compiler must deduce type from bar. But in your example no more additional info for compiler, that is why it just try to deduce.

@Johannes Schaub - litb 2010-02-25 17:25:44

@Dewfy, i don't see your point either. How is this different from int a(int); template<typename T> T g(T); template<typename T> void f(T); int main() { f(a(g(a(0)))); } ?

Related Questions

Sponsored Content

3 Answered Questions

10 Answered Questions

[SOLVED] Can lambda functions be templated?

1 Answered Questions

Call lambda with other lambda result when its return type is void

2 Answered Questions

2 Answered Questions

[SOLVED] Invalid use of incomplete type with templates and decltype

2 Answered Questions

1 Answered Questions

5 Answered Questions

[SOLVED] Generic way to deduce the return type of a functor?

2 Answered Questions

[SOLVED] How is type deduced from auto return type?

  • 2012-08-02 10:29:24
  • BЈовић
  • 849 View
  • 8 Score
  • 2 Answer
  • Tags:   c++ c++11 decltype

Sponsored Content