I stumbled on a strange interaction between typedef
and variadic template parameters that I'd like to understand. The following code compiles with clang but gives an error with GCC:
template<typename T> // no error if this is not a template
struct Traits;
#pragma GCC diagnostic ignored "-Wunused-parameter"
template<typename ...args>
void function(args... e) {}
template<typename T>
struct Caller {
typedef typename Traits<T>::types traits_types; // no error if this is changed to a 'using' directive
template<typename ...types> // no error if the pack is converted to a single parameter
static void method(types... e) {
function<traits_types>(e...);
}
};
GCC behavior
When I compile (not link) this with GCC, I get an error on line 14:
$ g++-9.2.0 -Wall -Wpedantic -Wextra -std=c++11 -c test.cpp
test.cpp: In static member function ‘static void Caller<T>::method(types ...)’:
test.cpp:14:31: error: parameter packs not expanded with ‘...’:
14 | function<traits_types>(e...);
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
test.cpp:14:31: note: ‘types’
$
It acts as if GCC first substitutes in the definition of traits_types
, which would produce
template<typename ...types>
static void method(types... e) {
function<typename Traits<T>::types>(e...);
}
and then evaluates template parameter substitution, at which point it sees the last occurrence of the token types
as an unexpanded parameter pack and produces an error accordingly.
I've tested this with GCC 6.4.0, 7.3.0, 8.2.0, 8.3.0, 9.1.0, and 9.2.0, as well as a couple older versions, and the behavior is consistent among all of them.
clang behavior
However, when I compile this with clang, it works fine.
$ clang++-8 -Wall -Wpedantic -Wextra -std=c++11 -c test.cpp
$
This seems as if it first substitutes in for the parameter pack types
and then handles the name traits_types
.
I see this behavior consistently in clang 6.0.0, 7.0.1, and 8.0.1, and a couple older versions.
Question
In standard C++11, is GCC correct to give the error that it does, or is the code valid? Or is it undefined/implementation-defined/otherwise unspecified?
I've looked through much of cppreference.com's content on templates and typedefs without finding anything that clearly addresses this case. I also checked several other questions (1, 2, 3, 4, 5, etc.), all of which seem similar but don't quite apply to this situation, as far as I can tell.
If this is in fact a compiler bug, a link to a relevant issue in the bug tracker confirming that GCC (or clang, if applicable) doesn't handle this correctly would settle the question pretty well.