Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.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++_Templates_C++11_Header_Forward Declaration - Fatal编程技术网

C++ 使用具有正向声明类型的模板-安全?

C++ 使用具有正向声明类型的模板-安全?,c++,templates,c++11,header,forward-declaration,C++,Templates,C++11,Header,Forward Declaration,我正在构建一个只包含标题的库,通过执行与代码所示类似的操作,我解决了一些循环依赖性问题 基本上,我创建了一个私有模板实现,它允许我使用前向声明的类型,就像它们被包含而不是被前向声明一样 我的方法有什么危险吗? 是否有性能损失?(库的主要焦点是性能-真正的代码有明确的内联建议) 额外问题:对编译时间有影响吗(正面还是负面)? //Component.h 结构实体;//远期申报 类组件 { 实体&实体; 模板void doimpl(){static_cast(entity).a(

我正在构建一个只包含标题的库,通过执行与代码所示类似的操作,我解决了一些循环依赖性问题

基本上,我创建了一个私有模板实现,它允许我使用前向声明的类型,就像它们被包含而不是被前向声明一样

我的方法有什么危险吗?

是否有性能损失?(库的主要焦点是性能-真正的代码有明确的
内联
建议)

额外问题:对编译时间有影响吗(正面还是负面)?



//Component.h
结构实体;//远期申报
类组件
{        
实体&实体;
模板void doimpl(){static_cast(entity).a();}
公众:

//void notWorking(){entity.a();}我建议将实现放在“*.inl”中

//实体.h

#ifndef ENTITY_H
#define ENTITY_H

class Component; // forward-declaration
struct Entity { void a(); };

#include "Entity.inl" 

#endif
//Entity.inl

#ifndef ENTITY_INL
#define ENTITY_INL

#include "Component.h";
inline void Entity::a() { /* implementation using Component and Entity */}

#endif
//第h部分

#ifndef COMPONENT_H
#define COMPONENT_H

struct Entity; // forward-declaration
class Component 
{        
    Entity& entity;
    public:
        void doA();
};

#include "Component.inl"

#endif
//Component.inl

#ifndef COMPONENT_INL
#define COMPONENT_INL

#include "Entity.h";
inline void Component::doA() { entity.a(); }

#endif

标题
Component.h
中的代码将无法编译,除非您还
#include Entity.h
。这将导致
Component.h
中出现神秘错误,如果您试图
单独包含Component.h
;更改
Entity.h
,使其不包含
的完整定义>实体
;或更改
库.h
,使其不再包含
#包含实体.h
。这通常被认为是不好的做法,因为此错误对于未来的代码维护者来说很难理解

clang给出
错误:成员访问不完整类型的“实体”

下面是一个演示错误的实例:

函数
doimpl()如果在不依赖模板参数的上下文中调用
,则会实例化它。在实例化时,
实体
用于类成员访问,因此需要完整。如果不
#包含Entity.h
,则类型
实体
在实例化时将不完整

实现您想要的目标的一个更简单(更漂亮)的方法是:

template<class Entity>
class Component 
{        
    Entity& entity;

    public:
        void doA() { entity.a(); } // this compiles fine
};
模板
类组件
{        
实体&实体;
公众:
void doA(){entity.a();}//这可以很好地编译
};
通常,(即使在仅页眉的库中)遵循这个简单的规则可以避免很多麻烦:每个头
name.h
必须在任何其他
\include
指令之前有一个匹配的
name.cpp
,它包含
\include name.h
。这保证了
name.h
可以安全地包含在任何地方,而不会导致此类错误

这是John Lakos在C++软件设计中引用的经典参考文献,引用Bruce Eckel在C++中的思想:

通过确保组件的.h文件自行解析(无需外部提供的声明或定义),可以避免潜在的使用错误…将.h文件作为.c文件的第一行,可以确保.h文件中没有缺少组件物理接口固有的关键信息(或者,如果有,您将在尝试编译.c文件时尽快了解它)

我的方法有什么危险吗?

事实上,只要模板实例化是延迟的,就不会出现太多错误。如果禁止错误的实例化,它可能会更好地声明意图:

typename std::enable_if< std::is_same< T, Entity >::value
    && sizeof ( T ) /* Ensure that Entity is not incomplete. */ >::type
另一种方法是简单地将
Entity.h
或任何东西作为依赖项,并将其包括在内。我认为这将是最流行的解决方案


如果
组件
确实不依赖于
实体
,那么
doA
可能属于一个派生类,它应该有自己的新头,其中包括两个现有头。

组件.h
中声明
doA
,并在
实体.h
中定义它就足够了




我做了一些类似的事情,但我没有打破使用前向声明类型可以做什么的规则。至于“性能影响”,没有。我甚至不知道为什么会有。如果你的“关注性能”的想法是“添加
内联
”,那么我就看到了这个目标的暗淡前景。好吧,那么请原谅我的热情。尽管我想知道-如果你知道这些事情,你不应该也知道解决性能问题最可靠的方法是查看汇编代码和/或基准测试吗?;-)在我看来,如果您的调用站点在调用
doA()之前包含
Entity.h
,那么您只能明智地使用此构造
。如果是这样,那么您最好直接包含该文件。您可以通过将函数定义添加到类定义之外来打破循环依赖关系。每当人们开始编写“游戏引擎”,出于性能考虑,常识不再适用。只需首先关注正确性和逻辑依赖性。如果您使用
pimpl
进行编译加速,并使用模板参数编写抽象接口,通常编译改进的空间不大。它同时使用
clang++3.4
g++4.8.1
。如问题中的一条评论所述,这项工作的要求是呼叫站点包括
Entity.h
@VittorioRomeo您可能需要仔细检查一下。我确信它可以工作。我的库由
组件.h
实体.h
管理器.h
组成。它还有一个
>LIBRARY.h
主要包含在外部项目中。
LIBRARY.h
只包含
组件.h
实体.h
管理器.h
。在外部项目中,当我编写
包含
时,我可以使用template<class Entity>
class Component 
{        
    Entity& entity;

    public:
        void doA() { entity.a(); } // this compiles fine
};
typename std::enable_if< std::is_same< T, Entity >::value
    && sizeof ( T ) /* Ensure that Entity is not incomplete. */ >::type
struct Entity;
void a( Entity & );

    void doA() { a( entity ); }
// Component.h

class Entity;

class Component
{
    void doA();
}
// Entity.h

class Entity { ... }

// still in Entity.h
void Component::doA() { entity.a(); }