26

Does anyone know if the following implicit capture of 'ts' is well-formed:

template<class ... Ts> void bar(Ts ... ts) { }

template<class ... Ts> int foo(Ts ... ts) {
    auto L = [=] () {
       bar(ts...);
    };
    L();
    return 0;
}
int g = foo(1, 2, 3);

Does the standard clearly state anywhere that this should not be well formed?

oblitum
  • 11,380
  • 6
  • 54
  • 120
Faisal Vali
  • 32,723
  • 8
  • 42
  • 45
  • I know you can capture them explicitly, but I haven't seen anything to the effect of "whatever you can capture explicitly, you can capture implicitly, and vice-versa" yet. – chris Jan 11 '13 at 03:03
  • If it is well formed, I believe it will result in infinite recursion. :-) I'm not sure what the standard says about that. I expect it's undefined behavior. ;-) If it is, that means parsing that code is allowed to have any result, even if the pack expression inside the lambda is well-formed. :-) – Omnifarious Jan 11 '13 at 03:37
  • thanks omnifarious - i fixed the infinite recursion issue. – Faisal Vali Jan 11 '13 at 03:44
  • It looks like GC 4.7.3 doesnt support it. – Nawaz Feb 10 '16 at 07:20

2 Answers2

13

14.5.3/6:

The instantiation of a pack expansion that is not a sizeof... expression produces a list E1, E2, ..., EN , where N is the number of elements in the pack expansion parameters. Each Ei is generated by instantiating the pattern and replacing each pack expansion parameter with its ith element. All of the Ei become elements in the enclosing list.

Regardless of whether you're allowed to explicitly capture a pack (you can, using [ts ...]), the general rule of expansion will result in capture of each element of the list.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 2
    or `[&ts...]`. Also, this paragraph tells about pack expansion rules, but didn't state anything helpful about rules of parameter packs being captured... IMHO, it does not answer the question. – oblitum Jan 11 '13 at 21:39
7

I guess it's well formed, I've not found a straight statement (the wording sometimes lacks in clarity / illustration for certain situations) but I guess it may be inferred:

§5.1.2/23:

A capture followed by an ellipsis is a pack expansion (14.5.3). [ Example:

      template<class... Args>
      void f(Args... args) {
        auto lm = [&, args...] { return g(args...); };
        lm();
      }

— end example ]

  • A capture followed by an ellipsis, implies args, in the lambda-capture, is an example of a capture (in this case, explicit), and the notable fact is that args is a parameter pack identifier. This short paragraph has the sole job of describing how lambda-captures hold pack expansions, which demonstrates parameter packs can be captured even though its purpose is not about allowing them to be captured.

§5.1.2/12:

An entity is captured if it is captured explicitly or implicitly.[...]

§3/3:

An entity is a value, object, reference, function, enumerator, type, class member, template, template specialization, namespace, parameter pack, or this.

From this I assume parameter packs are entities that can be captured explicitly or implicitly, and so, the same capturing rules as for ordinary variables shall apply, except that parameter packs shall be expanded accordingly.

I guess your question (and the same argumentation) could be applied equally well for reference variables for example (It is unspecified whether or not a reference requires storage. §8.3.2/4). It seems you're interested when you're allowed or not to refer to a parameter pack identifier inside a lambda.

You can think the same about reference variables in the outer scope since you may have access to them but couldn't even be allowed to access the identifier of the original variable.

They're as ethereal as parameter packs.

oblitum
  • 11,380
  • 6
  • 54
  • 120
  • *capture* is a grammatical construct defined at the beginning of the clause: *capture*: *identifier* | `&`*identifier* | `this`. There are few general terms as such in the standard… and an ellipsis needs to go somewhere in the grammar. Although implicitly capturing a variable involves naming it with an identifier, that doesn't imply such a subexpression forms a *capture*. Moreover the *capture* production doesn't match an expansion over a larger expression such as `std::forward(o)...`, so, 5.1.2/12 can't apply. – Potatoswatter Jan 12 '13 at 02:49
  • @Potatoswatter, I didn't get what you're trying to point out, sorry. I've highlighted 5.1.2/12 just to enforce that, even though the standard divides captures in two kinds there, these two can still be refereed as captures in the broader sense and so 5.1.2/23 is about both, not one specially. Also, a parameter pack can only be referred by an identifier, so I don't see an issue with that clause. – oblitum Jan 12 '13 at 03:46
  • Italicized terms refer to grammatical constructs only. "*Capture*" is a different thing from "a captured entity," no room for refereeing. Sorry, I meant 5.1.2/23 can't apply, not 5.1.2/12. – Potatoswatter Jan 12 '13 at 07:36
  • @Potatoswatter, about your first comment, when I say "implies args, in the lambda, is an example of a capture" I talk about `args` at `[]`. When not present there, a pack expansion inside the lambda body implies the same `args...` is implicit in the `[]`, when it's the case of it be implicitly captured. – oblitum Jan 12 '13 at 12:33
  • @Potatoswatter, I may be really confused about what you mean about "5.1.2/23 doesn't apply". If that clause doesn't serve to explain how implicitly parameter pack's are captured, I don't see it helping with explicits either, and so the paragraph has no purpose. – oblitum Jan 12 '13 at 12:41
  • It describes the handling of the production *capture-list*: *capture* `...`. It may indeed serve no purpose, since the section on parameter packs also mentions that pack expansions may occur inside a *capture-list*. However, (not referring back) I don't believe that clause specs *what part* is a pack expansion. It can be considered relatively minor, without purpose to semantics, yet not totally pointless. As for "the same `args...` is implicit in the `[]`," you're extrapolating too much. That's a reasonable way of thinking about this, but not how it's specified. – Potatoswatter Jan 12 '13 at 12:53
  • there is no reason or need to imagine any invisible syntax behind such a semantic construct as implicit capture. Syntax maps to semantics, not vice versa. – Potatoswatter Jan 12 '13 at 13:08
  • likewise there's no need to deduce. "Employing that syntax" is explicit, yet the syntax is not required and implicit capturing does not work like that. If the tokens aren't in the source file, the syntax isn't constructed anywhere else, because a clause would have to construct it and C++ is seldom specified in those terms. (Range-based for is a notable example, see that for contrast.) – Potatoswatter Jan 12 '13 at 13:14
  • @Potatoswatter, I've just made up my mind about some of your points, thanks, changed my deduction process in the answer accordingly. – oblitum Jan 12 '13 at 21:47
  • A function parameter pack can't be odr-used, and therefore can't be implicitly captured. But after the template instantiation replaces the function parameter packs, the expanded expression does odr-use each of the expanded function parameters, which are each implicitly captured. – aschepler Jan 13 '13 at 13:56
  • @aschepler I agree with that, but rules in the level of the parameter pack identifier itself must apply. It can be thought of just by declaring a lambda local ordinary variable with same identifier as of a parameter pack which is implicitly captured. By that I mean parameter pack identifiers have equal relevance as any other. – oblitum Jan 13 '13 at 14:08
  • @chico: I guess we agree on "Yes, it works like you'd expect" but disagree on whether "The function parameter pack is implicitly captured" is strictly supported by the Standard's wording? I don't see any point in arguing much about that. – aschepler Jan 13 '13 at 14:12
  • @aschepler I don't see it too, I feel it's a delicate point, the points I show in my answer is my argumentation against this point. Also, the standard state, a function parameter pack is a pack expansion, but note that, it's that, but it doesn't mean it leaves from being a parameter pack, it's both, and quality of both should be analysed. By being a parameter pack it's also an entity, as `this` is one too. – oblitum Jan 13 '13 at 14:16