GCC 7.3.1 compile the below code while clang 8.0.0 does not. I would like to know if this syntax is valid (in which case I will report it as a possible clang bug).
Thanks for your help.
template<typename FOO>
struct Foo
{
using Value = int;
template<Value VALUE>
struct Bar;
};
template<typename FOO>
template<typename Foo<FOO>::Value VALUE>
struct Foo<FOO>::Bar { static void test(); };
template<typename FOO>
template<typename Foo<FOO>::Value VALUE>
void Foo<FOO>::Bar<VALUE>::test() {}
int main() { return 0; }
The error message with clang is the following:
error: nested name specifier 'Foo<FOO>::Bar<VALUE>::' for declaration does not refer into a class, class template or class template partial specialization
void Foo<FOO>::Bar<VALUE>::test() {}
~~~~~~~~~~~~~~~~~~~~~~^
1 error generated.
EDIT: clang possible bug report here.
C++ templates can't use normal run-time C++ code in the process of expanding, and suffer for it: for instance, the C++ factorial program is limited in that it produces 32-bit integers rather than arbitrary-length bignums.
The short answer is no. Indirectly, however, they can slow things down under a few circumstances. In particular, each instantiation of a template (normally) produces code that's separate and unique from other instantiations.
The main type of templates that can be implemented in C are static templates. Static templates are created at compile time and do not perform runtime checks on sizes, because they shift that responsibility to the compiler.
C++ templates are checked at least twice. First, when a template is declared & defined, second when it is instantiated. After a template successfully instantiated it is in a type safe state.
From [temp.mem.class/1], we have
A member class of a class template may be defined outside the class template definition in which it is declared.
Furthermore, in a non-template context, [class.nest/2] tells us:
Member functions and static data members of a nested class can be defined in a namespace scope enclosing the definition of their class.
Let's hence construct a simpler example and verify that the definition of a member function of a nested type is allowed to be separated from the definition of the nested, non-template type itself. In analogy to the types in your snippet:
template <class FOO>
struct Foo {
// Simpler, Bar is not a template
struct Bar;
};
// Definition of Bar outside of Foo as before
template <class FOO>
struct Foo<FOO>::Bar {
static void test();
};
And now the critical part, the definition of Bar::test()
outside of Bar
itself:
template <class FOO>
void Foo<FOO>::Bar::test() { }
This happily compiles with both gcc-8
and clang
(trunk as well as a much older stable version).
I might be misunderstanding something here, but my conclusion is that the syntax to define Foo::Bar::test()
outside of Foo
and outside of Bar
is indeed fine, and clang
should compile it as gcc
does.
This is an interesting case! My position whether it is a compiler or standard problem is similar to @lubgr, but I wanted to add some more insights.
ICC also have some problems with your construct, which might suggest that this is more deeply rooted in standard (still, gcc might be correct here). It fails with error: "template argument list must match the parameter list" - this might mean that for both compilers this:
template<typename FOO>
template<typename Foo<FOO>::Value VALUE>
are not identical with original definition of Foo
. It seems to be a bug of both compilers, but I've learned to be cautious when two different compilers share similar problems.
Extracting definition of Value
from original template to separate one fixes the case (code on Compiler Explorer):
template<typename T>
struct X
{
using Value = int;
};
template<typename FOO>
struct Foo
{
template<typename X<FOO>::Value VALUE>
struct Bar;
};
template<typename FOO>
template<typename X<FOO>::Value VALUE>
struct Foo<FOO>::Bar { static void test(); };
template<typename FOO>
template<typename X<FOO>::Value VALUE>
void Foo<FOO>::Bar<VALUE>::test() {}
int main() { return 0; }
You can fix this as well by simply using hardcoded Value
type (code on Compiler Explorer) - but this is not what you need probably:
template<typename FOO>
struct Foo
{
template<int VALUE>
struct Bar;
};
template<typename FOO>
template<int VALUE>
struct Foo<FOO>::Bar { static void test(); };
template<typename FOO>
template<int VALUE>
void Foo<FOO>::Bar<VALUE>::test() {}
int main() { return 0; }
Hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With