0

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?

13
  • 8
    If you want to capture by reference why not use [&]() instead of [=]() which is by value? Commented Oct 21, 2020 at 14:41
  • 1
    "capture by reference by [=] value" -> "capture a reference by [=] value" ? Is this a typo? Commented Oct 21, 2020 at 14:41
  • 1
    A reference is just another name for an object. Your lambda captures everything by value, and that includes that object, no matter how its named. Commented Oct 21, 2020 at 14:45
  • @CoryKramer Because that particular reference will be out of scope by the time the lambda function is called, I'm pretty sure. If I'm capturing the reference by reference, the reference will be invalid once the function exits, right? Commented Oct 21, 2020 at 14:45
  • 1
    Here is explained what's going on: stackoverflow.com/questions/19676473/… Because it cannot be copied, it will not work. Commented Oct 21, 2020 at 14:50

2 Answers 2

3

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).

Sign up to request clarification or add additional context in comments.

3 Comments

"and references to objects are captures as copies of the referenced objects", how is having a lambda member AClass& aRef; copying the referenced object? Like even passed to the constructor, how is a copy involved?
The reference is stripped. If it weren't, things like capturing *this wouldn't work, as dereferencing a pointer yields an lvalue reference. Also, we'd have dangling reference issues with prvalues, e.g. godbolt.org/z/eG6sWr
Think of a capture by value as using auto: if you did auto v = aRef, you'd have the same issue.
1

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.

4 Comments

If aRef goes out of scope by the time the function is called, the reference inside the lambda is invalid, right?
@Zebrafish The reference in the lambda is a reference to the actual object, not to aRef.
Correct, dangling references are a problem to consider when doing by-reference captures. If the lambda outlives the referenced value, you have undefined behavior if you use it.
@ChrisUzdavinis That's OK, I thought capturing the reference by reference was different to capturing the object itself by reference. Apparently this isn't the case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.