4

Is it possible to write a type such that it can't implicit capture itself this with a lambda inside itself:

[&]{}

I currently have a situation where a temporary object is used as a factory to create another object. Inside this a lot of lambda's exist - it would be great to avoid capturing data members by mistake.

(note that this is also about future proofing the code a bit as this can result in some very hard to find bugs - another programmer can come along and not realize this quite easily)

(also note, it would actually be great to capture everything with [&] in this case as I actually do want to capture everything else by reference as this binds correctly to all the new stuff being created by the factory - [=] would be incorrect.)

darune
  • 10,480
  • 2
  • 24
  • 62
  • 1
    related: https://stackoverflow.com/questions/62821083/is-it-possible-to-detect-if-a-lambda-has-this-in-the-capture-group – parktomatomi Nov 05 '20 at 09:38
  • The problem is that the lambda might keep a pointer to a member of that (out of scope) temporary class. Is that correct? (in that case, perhaps you can use `[=]` everywhere?) – user202729 Nov 05 '20 at 09:44
  • Not an answer to the question (I've never found a solution to the question as asked) but, almost invariably, when faced with such a problem, an approach I used was to *explicitly* write a struct/class type with an `operator()` rather than generating one *implicitly* as a lambda. Doing it explicitly meant I had complete control over what members the struct/class had, rather than trying to prevent something being captured. – Peter Nov 05 '20 at 09:48
  • @user202729 that is correct. Although [=] is not a 'good' solution. Another guy might come along and screw up. Also It would actually be nice to use [&], but without the liability. – darune Nov 05 '20 at 09:49
  • @Peter Good point, but this doesn't remove the hazard that another guy may not realize the 'hazard' when eg. adding a new lambda. Hence the question stands. – darune Nov 05 '20 at 09:51
  • Don't think it's possible (see also [c++ - Is it possible to disallow taking a reference to an object - Stack Overflow](https://stackoverflow.com/questions/13236048/is-it-possible-to-disallow-taking-a-reference-to-an-object)), other than having something-else to check for it. (git pre-commit hook?) – user202729 Nov 05 '20 at 09:51
  • Then `[*this, &]`? – user202729 Nov 05 '20 at 09:56
  • @darune - The "hazard" you describe comes from poor use of lamdas (lambdas with captures creating more lambdas with captures to some depth). There is a point when it is better to find alternatives (like an actual class/struct type, like I mentioned, but there are other options) than trying to work around problems due to overusing lambdas. Frankly, I think you're well past that point. (It's probably also the reason that there isn't really a solution to your question ask asked - working around deficiencies of a design tends to make the language and compilers unnecessarily complicated). – Peter Nov 05 '20 at 10:46

2 Answers2

2

I don't really see a way to either forbid capturing this or detect it in the language (I don't really see a type trait for this, even with more creative use). Maybe there is tooling for this, but I haven't found anything. I think you have to overthink your design in some way:

  • Do not capture with [&]. Capturing all by reference is nice for local lambdas that never leave the scope, but personally, I would never use it for any lambda that is returned from the function.
  • Delegate lambda construction to a non-member function (but this is only "Do not use [&]" with extra steps)
  • If you do not want to capture this, why is the factory even an object? I do not really see, where you would use that then. Refactor the whole thing to use a static member function or even a free function (C++ ain't Java, namespaces with free functions are a great thing).
  • Not so robust, but somewhat working: Use a very recognizable naming scheme for the factory member variables (i.e. F_*), then you will recognize immediately if you are using one.

(Personally, I prefer the third point. In my opinion, classes are overused for such stuff)

EDIT: Seems like there was a pull request to llvm that adds a warning for this (or at least for captured local variables, I am unsure how that resolves with member variables). The change was seen positively but got lost on track, so it seems like someone just needs to rebase and review it to make it current. https://reviews.llvm.org/D24639

n314159
  • 4,990
  • 1
  • 5
  • 20
  • Capturing local variables can be fine. It depends on lifetime. – darune Nov 05 '20 at 10:47
  • I am not sure on what you are referring. I think I never said that capturing locals were not fine (I explicitly state that is totally fine for local lambdas). And the change I linked warns against *returning* lambdas that capture local variables by reference and that is most probably bad. – n314159 Nov 05 '20 at 11:04
  • The factory is abstract - it's the "Abstract Factory" pattern basicly (although one part uses std::variant for polymorphism, but not the abstract factories) – darune Nov 05 '20 at 12:29
  • You can still dispatch from the virtual factory classes to free functions or use function pointers. But making further guesses as to what is a reasonable choice for your use case is probably not sensible without much more information on the specifics and out of scope of this question. – n314159 Nov 05 '20 at 12:56
0

If it is important, you ought to explicitly write each variable you capture by reference/copy instead of just spamming [&] for all lambda captures.

What if they used a variable in the lambda and then used the lambda after the variable got destroyed? Isn't it the same issue as capturing this with usage of member variables? You just need to be very careful when capturing variables if you intend to forward the lambda elsewhere and stop with the bad practice of capturing everything without thinking.

Note: If you don't use any member variable then this isn't captured via [&].

ALX23z
  • 4,456
  • 1
  • 11
  • 18
  • I know the dangers of capturing, but this doesn't alleviate the fact I do actually really want to use [&] badly, but without the hazard of mistakenly taking some member from 'this' - I hope you see my problem ? But Im starting to realize i probably need some other design to avoid the situation.. – darune Nov 05 '20 at 10:11
  • @darune I'm just confused how reference capture `[&]` is only troublesome when capturing `this`? Wouldn't anything you capture cause a serious bug if lambda was used elsewhere? – ALX23z Nov 05 '20 at 10:19
  • Not really, its a reference to all the stuff being created that gets returned by the factory. – darune Nov 05 '20 at 10:37
  • @darune no... the stuff is captured by reference so it gets invalidated once the lambda leaves the scope even if it is a pointer to somewhere else. – ALX23z Nov 05 '20 at 10:44