2

GCC doesn't compile this code, while other compilers (clang, msvc) do

template<auto T>
struct S { };

int main() {
  S<[]{int x,y; x<<y;}> s1; // compiles fine
  S<[]{int x,y; x>>y;}> s2; // error
}

error:

error: expected ';' before '>>' token
      |     S<[]{int x,y; x>>y;}> s2;
      |                    ^~
      |                    ;

However when I explicitly call operator>>, GCC accepts it

struct S2 {
  void operator>>(S2 arg) { }
};
S<[]{S2 x,y; x.operator>>(y);}> s2; // compiles

It also does compile when I move the lambda definition outside of template parameter list

auto lam = []{int x,y; x>>y;}; 
S<lam> s; // compiles

Is it a compiler bug?

user3840170
  • 26,597
  • 4
  • 30
  • 62
michuu
  • 315
  • 4
  • 10

1 Answers1

6

g++ is actually correct here. From [temp.names]/3 (C++20 Draft N4860):

When a name is considered to be a template-name, and it is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator. When parsing a template-argument-list, the first non-nested > is taken as the ending delimiter rather than a greater-than operator. Similarly, the first non-nested >> is treated as two consecutive but distinct > tokens, the first of which is taken as the end of the template-argument-list and completes the template-id. [Note: The second > token produced by this replacement rule may terminate an enclosing template-id construct or it may be part of a different construct (e.g., a cast). — end note] [Example:

template<int i> class X { /* ... */ };
X< 1>2 > x1;                            // syntax error
X<(1>2)> x2;                            // OK

template<class T> class Y { /* ... */ };
Y<X<1>> x3;                             // OK, same as Y<X<1> > x3;
Y<X<6>>1>> x4;                          // syntax error
Y<X<(6>>1)>> x5;                        // OK

end example]

The >> is treated as two > tokens. You can wrap it in brackets to enforce what you are trying to do if you want.

template<auto T>
struct S { };

int main() {
  S<[]{int x,y; (x>>y);}> s2; // now compiles fine
}

Note: it may be that both accepting or rejecting is correct, since it's a bit vague what is meant by nested. See comments.

ChrisMM
  • 8,448
  • 13
  • 29
  • 48
  • I am not so sure this is a correct reading of the standard, though. It seems to hinge on a rather… not *indisputably* correct interpretation of the admittedly vague term ‘non-nested’. (Why should one not expect the curly brackets delimiting the lambda body to ‘nest’ the token if plain parentheses do?) I’d rather consider this a case of underspecification, if not a compiler bug. – user3840170 Nov 02 '22 at 18:12
  • @user3840170, I tried looking up the definition of _nested_, but could not find one, other than the footnote: _A > that encloses the type-id of a dynamic_­cast, static_­cast, reinterpret_­cast or const_­cast, or which encloses the template-arguments of a subsequent template-id, is considered nested for the purpose of this description._ I'd assume that nested would mean another `<` in this context. Their examples do specify using `(` and `)` to prevent the error though. It's definitely rather strange. It may be that it's okay to accept or reject, due to the ambiguity. – ChrisMM Nov 02 '22 at 18:41
  • Footnotes and examples are non-normative AFAIK, though I think it’s okay to use them for disambiguation, as in this case… although they don’t seem to disambiguate *enough* here. – user3840170 Nov 02 '22 at 18:47
  • @user3840170, I added a note to the end of my answer. It is definitely an odd case. – ChrisMM Nov 02 '22 at 18:52
  • @user3840170 Looks like `()` and `[]` don't require extra brackets on any compiler, only GCC requires ones for `{}`. Every compiler requires extra brackets for `<>`. http://coliru.stacked-crooked.com/a/f728b0941c8b67c0 Tested it with gcc 12.2; clang 15.0.0; msvc v19.33. – michuu Nov 02 '22 at 20:02