C ++,删除类头</string> </vector>中的#include <vector>或#include <string>

时间:2009-05-28 13:05:22

标签: c++ templates include

如果可能,我想删除&lt; vector&gt;的包含内容和&lt; string&gt;从我的类头文件。 string和vector都是头文件中声明的函数的返回类型。

我希望我可以做类似的事情:

namespace std {
    template <class T>
    class vector;
}

并且,在标题中声明向量并将其包含在源文件中。

是否有参考文献涵盖您必须包含在标题中的情况,以及您可以将包含纳入源文件的情况?

12 个答案:

答案 0 :(得分:14)

您无法安全地转发声明STL模板,至少如果您想要便携且安全地执行它。该标准清楚地表明了每个STL元素的最低要求,但为实现扩展留出了空间,可以添加额外的模板参数,只要它们具有默认值。也就是说:标准规定std :: vector是一个模板,它至少需要2个参数(类型和分配器),但在符合标准的实现中可以有任意数量的额外参数。

不包括字符串和向量标题有什么意义?当然,无论谁打算使用你的课程,都必须已经包含它,因为它在你的界面上。

当您询问有关何时包含以及何时转发声明的参考时,我的建议是:包括您界面中的所有内容,转发声明内部详细信息。

这里有更多的问题,简单的编译性能。如果您推送在标头之外的公共(或受保护)接口中的类型的包含,您将创建包含顺序的依赖项。用户必须知道在包含标题之前必须包含 string ,因此您需要为他们提供一件令人担心的事情。

实现文件中应包含哪些内容:实现细节,记录器,不影响接口的元素(数据库连接器,文件头),内部实现细节(即使用STL算法实现不会影响您的界面,为简单目的而创建的仿函数,实用程序......)

答案 1 :(得分:11)

除少数例外情况外,您不允许向std添加内容:命名空间。对于像vector和string这样的类,你没有选择,只能#include相关的标准头文件。

另外,请注意字符串不是类,而是basic_string<char>的typedef。

答案 2 :(得分:6)

这对矢量或字符串没有帮助,但值得一提的是iostream有一个前向引用标题,称为iosfwd。

答案 3 :(得分:4)

标准容器通常具有其他默认模板参数(分配器等),因此这不起作用。例如,这是GNU实现的一个片段:


  template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class vector : protected _Vector_base<_Tp, _Alloc>
    { ... };

答案 4 :(得分:4)

这也是我之前尝试做的事情,但由于模板原因,这是不可能的。

请参阅我的问题:Forward Declaration of a Base Class

由于这样的标题在开发过程中不会改变,所以无论如何都不值得优化......

答案 5 :(得分:4)

没有简单明显的方法(正如其他人已经很好地解释过的那样)。

但是这些标题应该被视为语言的一部分(真的!),所以你可以把它们放在你自己的标题中,没有任何问题,没有人会抱怨。

如果您的关注点是编译速度,我建议您使用预编译的头文件并将这些std头文件放入其中(除此之外)。它将显着提高您的编译速度。

对不起,对于“真正的赢家是避免战斗的人”的回答。

答案 6 :(得分:3)

如果字符串和向量仅用于您班级的非公开成员的签名,则可以使用PImpl习语:

// MyClass.h
class MyClassImpl;
class MyClass{
public:
    MyClass();
    void MyMethod();
private:
    MyClassImpl* m_impl;
};

// MyClassImpl.h
#include <vector>
#include <string>
#include <MyClass.h>
class MyClassImpl{
public:
    MyClassImpl();
    void MyMethod();
protected:
    std::vector<std::string> StdMethod();
};

// MyClass.cpp
#include <MyClass.h>
#include <MyClassImpl.h>

void MyClass::MyMethod(){
    m_impl->MyMethod();
}

您始终在头文件中包含vector和string,但仅限于类的实现部分;仅包含MyClass.h的文件不会拉入字符串和向量。

答案 7 :(得分:3)

只需在您引用STL集合的任何文件中包含标题。

正如其他人所提到的,没有办法可靠地转发声明STL类,即使你找到了一个特定的实现,如果你使用不同的STL实现,它可能会中断。

如果编译单元没有实例化类,它将不会使您的目标文件更大。

答案 8 :(得分:2)

警告

预计这样做会引起骚动。

该语言允许您派生自己的类:

// MyKludges.h
#include <vector>
#include <string>
class KludgeIntVector : public std::vector<int> { 
  // ... 
};
class KludgeDoubleVector : public std::vector<double> {
  // ...
};

class KludgeString : public std::string {
  // ...
};

更改您的功能以返回KludgeString和KludgeIntVector。由于这些不再是模板,您可以在头文件中转发声明它们,并在实现文件中包含MyKludges.h。

严格地说,派生类不会继承基类构造函数,析构函数,赋值运算符和朋友。您将需要提供(正常)任何您正在使用的实现。


// LotsOfFunctions.h
// Look, no includes!  All forward declared!
class KludgeString;
// 10,000 functions that use neither strings nor vectors
// ...
void someFunction(KludgeString &);
// ... 
// Another 10,000 functions that use neither strings nor vectors

// someFunction.cpp
// Implement someFunction in its own compilation unit
// <string> and <vector> arrive on the next line
#include "MyKludges.h"
#include "LotsOfFunctions.h"
void someFunction(KludgeString &k) { k.clear(); }

答案 9 :(得分:1)

也许你最好使用pimpl习语:在我看来,你不希望将你的类的实现暴露给客户端代码。如果向量和字符串对象按值聚合,则编译器需要查看其完整声明。

答案 10 :(得分:1)

除了向std::swap添加重载(我现在能想到的唯一例外)之外,通常不允许向std命名空间添加任何内容。即使允许,std::vector的实际声明也比OP中的代码复杂得多。有关示例,请参阅Nikolai N Fetissov's answer

除此之外,您还有一个问题,即您的班级用户将使用返回std::vectorstd::string的函数执行哪些操作。 C++ Standard section 3.10表示返回此类对象的函数返回 rvalues ,而rvalues必须是完整类型。在英语中,如果您的用户想要对这些功能执行任何操作,则必须#include <vector><string&gt;无论如何。我认为#include文件中的.h标题会更容易,并且可以使用它完成。

答案 11 :(得分:1)

我认为你的目标是加快编译时间?否则我不确定你为什么要删除它们。

另一种方法(不漂亮但实用)是在包含本身周围使用宏观保护。

e.g。

#ifndef STDLIB_STRING
#include <string>
#define STDLIB_STRING
#endif

虽然这看起来很混乱,但在大型代码库上确实会增加编译时间。我们所做的是创建一个自动生成防护的Visual Studio宏。我们将宏绑定到一个键以便于编码。然后它就成了公司编码标准(或习惯)。

我们也为我们自己的包括。

e.g。

#ifndef UTILITY_DATE_TIME_H
#include "Utility/DateTime.h"
#endif

由于我们在创建自己的头文件时有Visual Studio助手来自动生成防护,因此我们不需要#define。宏知道它是内部包含因为我们总是使用

#include ""

我们自己的包含和

格式

#include <>

for external includes。

我知道它看起来并不漂亮,但它确实加快了我们在较大的代码库上编译时间超过半小时(从内存中)。