c++templates

Why is non-type template parameter expression handling inconsistent across compilers?


Here is something I observed across various compilers. It seems there are compiler bugs.

template <int I>
struct X
{ };

int main(void)
{
  X<(16 > 1)> a;       // Works on vc9, works on g++ 4.1.2, works on Comeau 4.3.10.1
  X<(int(16) > 1)> b;  // Works on vc9, works on g++ 4.1.2, works on Comeau 4.3.10.1
  X<(16 >> 1)> c;      // Works on vc9, works on g++ 4.1.2, works on Comeau 4.3.10.1
  X<(int(16) >> 1)> d; // Fails on vc9, works on g++ 4.1.2, works on Comeau 4.3.10.1

  X<16 > 1> e;         // Fails on vc9, works on g++ 4.1.2, fails on Comeau 4.3.10.1
  X<int(16) > 1> f;    // Fails on vc9, fails on g++ 4.1.2, fails on Comeau 4.3.10.1
  X<16 >> 1> g;        // Fails on vc9, works on g++ 4.1.2, fails on Comeau 4.3.10.1
  X<int(16) >> 1> h;   // Fails on vc9, works on g++ 4.1.2, fails on Comeau 4.3.10.1
 }

Why is that inconsistency? What is allowed/disallowed by the standard? Such behavior is also responsible for syntax error while using BOOST_AUTO on vc9. It appears to me that Comeau is doing the right job by rejecting all the expressions without parenthesis.


Solution

  • The rules are as follows for C++03:

    After name lookup (3.4) finds that a name is a template-name, if this name is followed by a <, the < is always taken as the beginning of a template-argument-list and never as a name followed by the less-than operator. When parsing a template-id, the first non-nested > [foot-note: 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. ] is taken as the end of the template-argument-list rather than a greater-than operator.

    So the result is:

      X<(16 > 1)> a;       // works
      X<(int(16) > 1)> b;  // works
      X<(16 >> 1)> c;      // works
      X<(int(16) >> 1)> d; // works
    
      X<16 > 1> e;         // fails
      X<int(16) > 1> f;    // fails
      X<16 >> 1> g;        // works (">>" is not a ">" token)
      X<int(16) >> 1> h;   // works (">>" is not a ">" token). 
    

    However, in C++0x the following are the rules

    After name lookup (3.4) finds that a name is a template-name, or that an operator-function-id refers to a set of overloaded functions any member of which is a function template, if this 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 > [foot-note: 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.] 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.

    Result will be

      X<(16 > 1)> a;       // works
      X<(int(16) > 1)> b;  // works
      X<(16 >> 1)> c;      // works
      X<(int(16) >> 1)> d; // works
    
      X<16 > 1> e;         // fails
      X<int(16) > 1> f;    // fails
      X<16 >> 1> g;        // fails (">>" translated to "> >")
      X<int(16) >> 1> h;   // fails (">>" translated to "> >")
    

    Be sure to disable C++0x mode in comeau when testing