Search code examples
c++lambdac++11visual-c++-2010decltype

Unable to instantiate function templates which uses decltype to deduce return type, if called from inside a lambda?


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?


Solution

  • 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.(?)