C++嵌套模板问题

C++ nested template issue

GCC 7.3.1 编译以下代码而 clang 8.0.0 不编译。 我想知道此语法是否有效(在这种情况下,我会将其报告为可能的 clang 错误)。

感谢您的帮助。

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; }

clang 的错误信息如下:

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.

编辑: clang 可能的错误报告 here.

[temp.mem.class/1]开始,我们有

A member class of a class template may be defined outside the class template definition in which it is declared.

此外,在非模板上下文中,[class.nest/2] 告诉我们:

Member functions and static data members of a nested class can be defined in a namespace scope enclosing the definition of their class.

因此让我们构建一个更简单的示例并验证允许将嵌套类型的成员函数的定义与嵌套的定义分开,非模板 类型本身。类似于您的代码段中的类型:

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(); 
};

现在是关键部分,Bar::test()Bar 本身之外的定义:

template <class FOO>
void Foo<FOO>::Bar::test() { }

这可以愉快地与 gcc-8clang(主干以及更旧的稳定版本)一起编译。

我可能在这里误解了什么,但我的结论是在 FooBar 之外定义 Foo::Bar::test() 的语法确实很好,并且 clang 应该像 gcc 那样编译它。

这是一个有趣的案例!无论是编译器问题还是标准问题,我的立场与@lubgr 类似,但我想补充一些见解。

ICC 对您的构造也有一些问题,这可能表明它更深入地植根于标准(不过,gcc 在这里可能是正确的)。它失败并出现错误:"template argument list must match the parameter list" - 这可能意味着对于两个编译器来说:

template<typename FOO>
template<typename Foo<FOO>::Value VALUE>

Foo的原始定义不同。这似乎是两个编译器的错误,但我学会了在两个不同的编译器共享类似问题时要谨慎。

从原始模板中提取 Value 的定义以单独修复此情况 (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; }

您也可以通过简单地使用硬编码 Value 类型 (code on Compiler Explorer) 来解决此问题 - 但这可能不是您需要的:

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; }

希望对您有所帮助!