简体   繁体   中英

C++ nested template issue

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 .

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM