【问题标题】:forward declaration of a class template including a namespace causes compile-error包含命名空间的类模板的前向声明导致编译错误
【发布时间】:2018-06-08 15:05:09
【问题描述】:

我有一个 example1 和一个 example2,它使用一个类模板的前向声明,前缀为相应类模板的命名空间。第一个示例使用 visual-studio 编译得很好,而第二个示例不能。我对照其他编译器(http://rextester.com)检查了这两个示例。现在我有两个问题:

  • 在前向声明中使用命名空间似乎是非法的。为什么?

  • Visual Studio(2015 和 2017)似乎允许在 first 示例中使用附加命名空间的前向声明,但在第二个示例中不允许。这是一个错误吗?

示例 1:

#include <vector>

namespace N1
{
    template <typename T> struct MySystem {};
    template <typename T> class Other {};

    struct MyClass
    {
        MyClass() { typename Dependencies::TYPE_A oTYPE_A; }

        struct Dependencies
        {
            template <typename>
            class N1::Other;

            struct TypeX_Dependencies;

            using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>;

            struct TypeX_Dependencies
            {
                using TYPE_A = typename Dependencies::TYPE_A;
            };

            using TYPE_X = N1::Other<TypeX_Dependencies>;
        };
    };
}

int main(){ return 0; }

c++ (gcc 5.4.0)
source_file.cpp:15:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;

c++ (clang 3.8.0)
source_file.cpp:15:23: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:15:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;

c++(vc++ 19.00.23506 for x64 / 还有 vs community 2017 15.4.4)
compiles fine

示例 2:

#include <vector>

namespace N1
{
    template <typename T> struct MySystem {};
    template <typename T> class Other {};

    template <typename T>
    struct MyClass
    {
        MyClass() { typename Dependencies::TYPE_A oTYPE_A; }

        struct Dependencies
        {
            template <typename>
            class N1::Other;

            struct TypeX_Dependencies;

            using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>;

            struct TypeX_Dependencies
            {
                using TYPE_A = typename Dependencies::TYPE_A;
            };

            using TYPE_X = N1::Other<TypeX_Dependencies>;
        };
    };
}

int main(){ return 0; }

c++ (gcc 5.4.0)
source_file.cpp:16:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;

c++ (clang 3.8.0)
source_file.cpp:16:23: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:16:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;

c++(vc++ 19.00.23506 for x64 / 还有 vs community 2017 15.4.4)
source_file.cpp(16): error C3855: 'N1::Other': template parameter 'T' is incompatible with the declaration
source_file.cpp(24): note: see reference to class template instantiation 'N1::MyClass<T>::Dependencies' being compiled
source_file.cpp(29): note: see reference to class template instantiation 'N1::MyClass<T>' being compiled
source_file.cpp(20): error C3203: 'Other': unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type

【问题讨论】:

  • 使用Other&lt;T&gt; 让它工作
  • struct Dependencies 中声明的Other 应该是一个新类,还是指全局::N1::Other 类?
  • Compiles for me。无论如何,你为什么觉得有必要首先在Dependencies 中重新声明Other
  • 把“typename”放在“Dependencies”前面。
  • 所以你真正想做的是class ::N1::Other,但由于该类已经可见,你根本不需要那个声明。您当前的声明试图引用(不存在的)::N1::MyClass::N1::Other 类。

标签: c++ templates compiler-errors namespaces forward-declaration


【解决方案1】:

是的,这是一个错误。

根据[dcl.type.elab]/1

...如果 elaborated-type-specifier 是声明的唯一组成部分,则声明格式错误,除非它是显式特化、显式实例化或具有以下形式之一:

  • 类键属性说明符序列opt标识符;
  • friend class-key ::opt标识符 ;
  • friend class-key ::optsimple-template-id ;
  • friend class-key nested-name-specifier identifier ;
  • friend class-key nested-name-specifier templateoptsimple-template-id ;

您的声明 class N1::Other 既不是显式特化,也不是显式实例化,因此它必须具有强调形式(忽略那些 friend 声明)。请注意,强调形式中的 identifier 之前不允许有 nested-name-specifier,因此声明格式不正确。下面没有模板的例子中Clang的编译错误就说明了这个问题。

namespace N {
    struct S;
}
struct N::S; // error: forward declaration of struct cannot have a nested name specifier

LIVE EXAMPLE(顺便说一句,GCC 接受此代码,只是警告说没有新的要声明的内容。我猜这是 GCC 的一个错误。)

这里template-headtemplate &lt;typename&gt;没有帮助,因为class N1::Other本身应该根据template-head的语法定义形成一个声明,因此上述段落适用。

粗略地说,与声明同名类的范围不同的类的声明应该引入一个新类,在这种情况下,nested-name-specifier不应该是使用,或定义先前声明的类,在这种情况下,语法形成class-specifier 而不是详细类型说明符,因此上面的段落不适用。总而言之,这条规则是合理的。

【讨论】:

    猜你喜欢
    • 2023-03-06
    • 2011-12-24
    • 1970-01-01
    • 2012-12-27
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 2013-08-31
    • 1970-01-01
    相关资源
    最近更新 更多