简体   繁体   中英

Why won't this lambda capture this variable with a deleted constructor by reference?

My compiler, Visual Studio 19, says "Attempting to reference a deleted function":

class AClass 
{public:
    AClass(const AClass& other) = delete;
    AClass() {}
    AClass(AClass&& other) { }
    AClass& operator=(const AClass& other) = delete;
    AClass& operator=(AClass&& other) { return *this; }
    int member;
};


void main()
{
    AClass a;
    AClass& aRef = a;

    [=]() { return aRef.member; }; // "Attempting to reference a deleted function"

}

I assume it's trying to call the copy constructor, which is deleted, but I don't understand why as I'm trying to capture by reference by [=] value, meaning I don't copy the object AClass, I don't see how any copy is involved. My understanding is that the lambda looks something like this:

struct lambda {
    AClass& refToAClass; // <--- this is the captured object
    lambda(AClass& captureVariable) : refToAClass(captureVariable) {}
    int operator()() const
    {
        return refToAClass.member;
    }
};


void main()
{
      // AND I CAN CONSTRUCT THE LAMBDA, NO COPYING OF AClass INVOLVED
      AClass a;
      AClass& aRef = a;

      lambda lam(aRef); // WORKS FINE
}

How is a copy involved in this case? And how do I capture that reference?

From the standard :

For each entity captured by copy , an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the referenced type if the entity is a reference to an object, an lvalue reference to the referenced function type if the entity is a reference to a function, or the type of the corresponding captured entity otherwise . A member of an anonymous union shall not be captured by copy.

cpprefence worded it better:

The type of each data member is the type of the corresponding captured entity, except if the entity has reference type (in that case, references to functions are captured as lvalue references to the referenced functions, and references to objects are captured as copies of the referenced objects ).

Perhaps this helps clarify what I think you're confused about:

template <typename T> void f(T arg) { }

int x = 10;
int& r = x;

auto y = r;     // y is int (copied value from reference)
f(r);           // T in f<T>() is deduced to int, arg in f() is int

In both cases if you want the result to be a reference, you must say so:

template <typename T> void f(T& arg) { }

int x = 10;
int& r = x;

auto& y = r;     // y is reference-to-int
f(r);            // T in f<T>() is int, arg in f is ref-to-int

Similarly, in your code:

AClass a;
AClass& aRef = a;

[=]() { return aRef.member; }; // "Attempting to reference a deleted function"

The captured aRef is an attempted COPY of the local variable from a reference to a different object. using [=] says to copy by value, and it does not read this as "oh, the source is a reference, so I'm going to copy the reference by value and store the reference in a reference." It actually says, "If the source type has reference qualifiers, ignore the and consider what's left. You want to copy by value? Ok, I'll copy an AClass into a new variable. Oh wait... can't. Sorry."

What you want is:

[&]() { return aRef.member; };

Or more specifically:

[&aRef]() { return aRef.member; };

That tells the captured type that it should be a reference. The source type is not as important as the destination type in cases like this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM