C ++嵌套模板问题

时间:2019-03-25 09:07:25

标签: c++ templates g++ language-lawyer clang++

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

带有叮当声的错误消息如下:

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.

编辑: 可能的错误报告here

2 个答案:

答案 0 :(得分:2)

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

  

类模板的成员类可以在声明它的类模板定义之外定义。

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

  

嵌套类的成员函数和静态数据成员可以在包含其类定义的名称空间范围内定义。

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

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一起编译(trunk以及较旧的稳定版本)。

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

答案 1 :(得分:1)

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

ICC的构造也存在一些问题,这可能表明它在标准中有更深的扎根(不过,gcc在这里可能是正确的)。它失败并显示错误:“模板参数列表必须与参数列表匹配”-这可能意味着对于两个编译器而言:

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

希望有帮助!