3

The nested call of variadic methods with an additional non-type template parameter (with a default value) of class templates results in compiler errors for clang and g++ (msvc compiles without problems) if the non-type template parameter of the called method is explicitly specified. The errors can be reproduced with the following minimal example:

#include <iostream>
#include <vector>

template< typename T >
struct S;

template< typename T >
struct SProxy
{
  template< int i = 20, typename... Ts >
  auto f( Ts&&... args );

  S< T >* s1;
  int     ii;
};

template< typename T >
struct S
{
  template< int i = 20, typename... Ts >
  auto f( int ii, Ts && ... args )
  {
    auto res = std::pair< int, int >( std::forward< Ts >( args )... );

    res.first  += i + ii;
    res.second += i + ii;

    return res;
  }
};

template< typename T >
template< int i, typename... Ts >
auto SProxy< T >::f( Ts&&... args )
{
  return s1->f< i >( ii, std::forward< Ts >( args )... );
}

int main( const int argc, const char* argv[] )
{
  S< int >      s;
  SProxy< int > sProxy{ &s, 3 };

  auto pair = sProxy.f< 10 >( 11, 12 );

  std::cout << pair.first << " " << pair.second << std::endl;

  return 0;
}

The error message produced by clang 9.0.0 is

<source>:36:53: error: expected ')'
return s1->f< i >( ii, std::forward< Ts >( args )... );

If the line return s1->f< i >( ii, std::forward< Ts >( args )... ); is altered to return s1->f( ii, std::forward< Ts >( args )... );, i.e., the default non-type template parameter is used, the code compiles (and the corresponding binary works) just fine for clang and g++.

My question is if the construct employed in the minimal example is not compliant to the C++17 standard and if so why, or if there is anything wrong with the syntax used and if so how to do it correctly, or (quite unlikely) if the errors are a result of bugs in clang and g++?


Edit: Wrong Compiler Explorer link removed

esakaen
  • 43
  • 3

1 Answers1

1

First of all

how to do it correctly [?]

Answer: add a "template"

// ........VVVVVVVVV   template added
return s1->template f< i >( ii, std::forward< Ts >( args )... );

there is anything wrong with the syntax used [?]

Yes: the compiler interpret the first < as a relational operator

// .........V  seen as relational operator ( s1->f less than i )
return s1->f< i >( ii, std::forward< Ts >( args )... );

so you have to instruct the compiler to consider the following < as the start of a template argument list. So the "template" added.

Off topic: from your godbolt link I see a completely different code with a completely different problem (related to constexpr).

max66
  • 65,235
  • 10
  • 71
  • 111