2

I'm using c++14, auto is working fine on .cpp file, however when i try to create generic lambda in my class it generates error "Auto is not allowed here"

What did i do wrong?

Code:

class MyClass{
private:

    // Works
    std::function<void(MyClass&)> assign = [&](MyClass& other){
        //do something
    };

    // Does not work
    std::function<void(auto)> assign = [&](auto other){
        //do something
    };

    // Does not work
    auto assign = [&](auto other){
        //do something
    };
};
Robert Tirta
  • 2,593
  • 3
  • 18
  • 37
  • 2
    Reopened because the [suggested duplicate](https://stackoverflow.com/questions/45632953/why-auto-is-not-allowed-as-function-argument) does not answer the question(s). – Quentin Oct 07 '20 at 12:43
  • Which `auto` does the error message refer to? AFAIK, `auto` cannot be used for non-static member variables: https://godbolt.org/z/bnqz89. – Daniel Langr Oct 07 '20 at 12:54
  • https://www.onlinegdb.com/online_c++_compiler also emits error when declared in cpp file – Serve Laurijssen Oct 07 '20 at 12:58

2 Answers2

2

Your two attempts fail for different reasons. Let's look at the second one first:

auto assign = [&](auto other){
    //do something
};

This could work in theory: the lambda expression has a definite type that could be deduced and used. However, as addressed in this Q&A (for C++11 but still relevant), the language simply does not allow auto member variables, so that's that.

Your first attempt fails for a more design-related reason. Well, the immediate issue is that void(auto) is not a valid type (auto is a syntactic keyword with tailored semantics, not a type), but one could conceivably use some tag types to make std::function<void(auto_t)> a valid specialization.

The issue, however, is that std::function is a type-erasure tool: its goal is to take in and conceal a functionoid (such as your lambda), then call it on demand. By the time the call is made, the functionoid's type is not known anymore, much less its potential operator() template -- handling the general case would require instantiating that template at runtime for a yet-unknown argument type, which is not possible.

Bottom line: storing the lambda is conceptually valid but its type deduction is not supported, and type erasure is the wrong tool for the job since templates are involved. Your only solution is to create an explicit functor class, and use that as a member:

class MyClass {
    struct {
        template <class T>
        void operator()(T other) const {
            // do something
        }
    } assign;
};

Bonus point: C++20 allows lambdas in unevaluated context and default-constructible lambdas, which means that the following insanity is now valid

class MyClass {
    decltype([](auto other) {
        // do something
    }) assign;
};
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Clear, concise, and answered all my questions! Thank you, its very interesting to see the rational where proposal for auto as member type is rejected. Different c++ versions have many changes and sometimes its confusing to see which one is which ._. – Robert Tirta Oct 08 '20 at 00:14
0

These statements are perfectly ok on a different context / scope:

void myfunc() {
    auto lambda = []() { };     // works fine
}

But the here you refer is a class definition, where there can't be members without explicit types.

class MyClass {
     
   std::function<void(MyClass&)> assign = lambda;
   // works, because there exists now a variable assign,
   // of specific type, which is initialised from a lambda

   auto kOne = 1;  // fails for the same reason
};
Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57