Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 虚拟函数、重写它们的类模板和不完整的类型 让我们考虑下面的代码片段(扩展到多个文件):_C++ - Fatal编程技术网

C++ 虚拟函数、重写它们的类模板和不完整的类型 让我们考虑下面的代码片段(扩展到多个文件):

C++ 虚拟函数、重写它们的类模板和不完整的类型 让我们考虑下面的代码片段(扩展到多个文件):,c++,C++,基础h: #include <iostream> class Base { public: virtual void foo () = 0; }; template <class D> class BaseImpl : public Base { public: BaseImpl () : d (new D ()) {} virtual ~BaseImpl () { delete d; } void foo () override; private:

基础h:

#include <iostream>

class Base
{
public:
  virtual void foo () = 0;
};

template <class D>
class BaseImpl : public Base
{
public:
  BaseImpl () : d (new D ()) {}
  virtual ~BaseImpl () { delete d; }
  void foo () override;
private:
  D * d;
};

template <class D>
void BaseImpl <D>::foo ()
{
  std::cout << d->value << std::endl;
}
main.cpp:

#include "Derived.h"

int main ()
{
    Derived d;

    d.foo (); // [1] error
    ((Base *) & d)->foo (); // [2] fine
}
此示例在第[1]行的CLang(Apple LLVM版本10.0.0(CLang-1000.11.45.5))上产生编译器错误,如下所示:

g++-c-std=c++11 main.cpp-o main.o
在main.cpp中包含的文件中:1:
在包含自./Derived.h的文件中:1:
./Base.h:22:17:错误:成员访问不完整的“数据”类型

std::cout value示例程序格式不正确

首先,检查任何模板的定义以获得与所有其他代码一样有效的语法,但是除非在模板被实例化之前,不需要C++实现来检查依赖于模板参数的任何部分的语义。(如果没有可能使专门化有效的模板参数,即使没有任何实例化,实现也可能报告语义错误。)

因此,如果main.cpp生成的翻译单元没有实例化函数模板专门化
BaseImpl::foo()
,那么程序就可以了。但是这个模板专门化具体是在什么时候实例化的呢

除非类模板或成员模板的成员已显式实例化或显式专门化,否则当在要求成员定义存在的上下文中引用专门化时,或者如果成员定义的存在影响语义,则隐式实例化成员的专门化是节目的一部分

定义存在的主要要求来自ODR规则:

每个程序应包含该程序中odr在丢弃语句外使用的每个非内联函数或变量的一个定义;无需诊断……内联函数或变量应在每个转换单元中定义,其中odr在丢弃语句外使用

在这里,我们说专门化
BaseImpl::foo()
是否为“内联”并不重要。ODR规则的两种风格最终都说,如果专门化是ODR使用的,那么必须定义它(以某种方式),这意味着专门化是隐式实例化的

对于不同种类的实体,技术术语“odr使用”有不同的定义(有时这些定义会重叠)。引用[basic.def.odr]/5-8中稍微超出必要的内容来说明一点:

变量
x
的名称显示为可能计算的表达式
ex
ex
使用的odr,除非

如果结构化绑定显示为可能的计算表达式,则使用odr

*如果
显示为潜在的计算表达式(包括非静态成员函数体中的隐式转换结果),则使用此

如果虚拟成员函数不是纯函数,则使用odr。如果函数由可能计算的表达式命名,则使用odr。类的非放置分配或释放函数是该类构造函数的定义使用的odr。类的非放置释放函数是该类的定义使用的odr该类的析构函数,或者通过在虚拟析构函数的定义点进行查找来选择

类中的赋值运算符函数是[class.copy.assign]中指定的另一个类的隐式定义的复制赋值或移动赋值函数所使用的odr。类的构造函数是[dcl.init]中指定的odr。如果类的析构函数可能被调用,则使用odr

请注意“使用odr”的所有这些定义将属性与某个特定的表达式或定义相关联,除非有人说非纯的虚拟函数仅由现有的odr使用。因此,在大多数情况下,隐式实例化是因为这些特定的其他表达式和定义需要模板专门化。这对于实例化而言意味着什么g虚拟功能在和中有所阐明:

如果虚拟成员函数不会被实例化,则未指定实现是否隐式实例化类模板的虚拟成员函数

如果虚拟函数是隐式实例化的,则其实例化点紧跟在其封闭类模板专用化的实例化点之后

因此,如果main.cpp根本没有提到
foo
,但它仍然需要类
BaseImpl
的实例化,而
Data
仍然是一个不完整的类型,那么程序是否有效将是未知的!所以即使这样也可能不是一个好主意

顺便说一句,这就是为什么允许来自Derived.cpp的翻译单元实例化
BaseImpl::foo()
。和g++,以及大多数使用公共“vtable”的编译器虚拟函数的策略,将实例化任何所需的类模板成员,这些成员是一个类的最终重写,只要该类的任何构造函数(显式或隐式)被调用已定义,因为它需要构建包含这些函数的vtable,以便构造函数可以将类对象的内部vptr设置为指向vtable

但我一开始说它的格式不正确,因为“否则将不会被实例化”所暗示的另一部分。另一句可能暗示
BaseImpl::foo()
is odr used的句子是“如果一个函数是由一个潜在的计算表达式命名的,那么它就是odr used”。在引用之前不久,我们发现“由表达式命名的函数”也是一个技术定义,在:

函数由表达式命名,如下所示:

  • 如果函数的名称出现在表达式中,则该函数的名称由该表达式命名(如果该函数是唯一的)
    #include "Derived.h"
    
    struct Data
    {
      int value = 123;
    };
    
    Derived::Derived () {}
    Derived::~Derived () {}
    
    #include "Derived.h"
    
    int main ()
    {
        Derived d;
    
        d.foo (); // [1] error
        ((Base *) & d)->foo (); // [2] fine
    }
    
    g++ -c -std=c++11 main.cpp -o main.o
    In file included from main.cpp:1:
    In file included from ./Derived.h:1:
    ./Base.h:22:17: error: member access into incomplete type 'Data'
      std::cout << d->value << std::endl;
                    ^
    main.cpp:7:5: note: in instantiation of member function 'BaseImpl<Data>::foo' requested here
      d.foo (); // [1]
        ^
    ./Derived.h:3:8: note: forward declaration of 'Data'
    struct Data;
           ^
    1 error generated.
    
    g++ -c -std=c++11 main.cpp -o main.o
    In file included from Derived.h:1:0,
                     from main.cpp:1:
    Base.h: In instantiation of 'void BaseImpl<D>::foo() [with D = Data]':
    main.cpp:7:10:   required from here
    Base.h:22:13: error: invalid use of incomplete type 'struct Data'
       std::cout << d->value << std::endl;
                 ^
    In file included from main.cpp:1:0:
    Derived.h:3:8: error: forward declaration of 'struct Data'
     struct Data;
            ^