【问题标题】:Defining templated functions in .cpp from .h (getting error)从 .h 在 .cpp 中定义模板化函数(出现错误)
【发布时间】:2012-11-19 11:11:53
【问题描述】:

部分头文件dlist.h定义为:

#ifndef __DLIST_H__
#define __DLIST_H__
#include <iostream>

class emptyList {};

template <typename T>
class Dlist {
 public:
    bool isEmpty() const;

 private:
    struct node {
    node   *next;
    node   *prev;
    T      *o;
    };

    node   *first; // The pointer to the first node (NULL if none)
    node   *last;  // The pointer to the last node (NULL if none)
};

#include "dlist.cpp"
#endif

当我像这样创建一个 dlist.cpp 文件时:

#include "dlist.h"

template <typename T>
bool Dlist<T>::isEmpty() const
{
    return !first and !last;
}

我在第 4 行收到错误消息:重新定义 'bool Dlist::isEmpty() const'

如果我删除 #include "dlist.h",我会在第 4 行收到错误:在 '

这里有什么帮助吗?我做错了什么,不允许我从 dlist.h 文件中定义我的函数吗?谢谢。

【问题讨论】:

  • 头文件中有一个},它不属于那里。我猜错了?
  • Defining templated functions in .cpp 不要!

标签: c++


【解决方案1】:

您必须将类模板的成员函数的实现放在头文件或头文件包含的文件中。编译器需要访问此代码才能实例化任何给定类型T 的模板。

在您的情况下,问题似乎是您将标题包含在 .cpp 中,反之亦然。如果您真的想将声明和实现保留在单独的文件中,我建议将实现的后缀从 .cpp 更改为其他内容,例如.icpp。一些构建系统可能会尝试使用带有.cpp 后缀的任何内容来编译目标文件,这也会导致错误。

  1. dlist.cpp 中删除#include "dlist.h"
  2. (可选)将dlist.cpp 重命名为dlist.icpp。为什么?因为许多构建系统会自动将任何以.cpp 结尾的文件编译为目标文件。许多程序员认为.cpp 文件会被编译成目标文件。
  3. (仅当采取第 2 步时)在 dlist.h 中包含重命名的 dlist.icpp,就像目前对 dlis.cpp 所做的那样。

【讨论】:

  • 我不允许更改头文件,那么在这种情况下我该怎么办?
  • 在后一种情况下,使用 .tpp 扩展名命名文件被认为是一种好的做法。
  • @BobJohn 从您的 .cpp 文件中删除 #include "dlist.h"。也许更改.cpp 的后缀,并将其包含在您的.h 中(就像您现在所做的那样)。
  • I'm not allowed to change the header file 有人给了你一个头文件,里面有一个类模板,没有定义它的实例化的成员函数,然后禁止你修改它?今天辞掉你的工作。
  • @BobJohn:他的意思是不要将它作为一个单独的单元编译——它的代码已经与任何代码一起被编译了'不单独建造。
【解决方案2】:

头文件是为我定义的,我不允许以任何方式更改它

然后你需要从.cpp 中删除#include "dlist.h" 指令(因为你已经 dlist.h,从而创建了一个循环依赖),从而使一切完全倒退,因为你得到的标题是愚蠢的!

.cpp 应该永远#included。通常,如果一个必须将与模板相关的定义拆分到他们自己的文件中,它应该有一些其他的扩展名。我强烈建议您与赋予您此任务的人交谈,并解释他们的标题很愚蠢、令人困惑且非常规。

【讨论】:

  • 以下是提供的注释: 注意:包含以下行,因为 g++ 编译器需要查看您的方法的“模板化”版本。这是唯一可以接受#include cpp文件的实例。
  • @BobJohn:嗯,注释是错误的。这是可以接受的。该文件的扩展名应不是.cpp
  • 有趣。你能详细说明为什么会这样吗?我很想知道为什么它会受到如此广泛的反对。
  • @BobJohn:正是出于这个原因。因为您在标头中包含.cpp,所以您需要小心不要.cpp 中包含.hpp,并配置(可能hack!) 您的构建系统不要将.cpp 编译为翻译单元。这是对通用约定的 100% 逆转,对于任何首次维护您的代码的人来说,这都会令您感到惊讶。如果文件有像 .ipp (或其他东西)这样的异常,我怀疑你一开始就没有对 #include "dlist.h" 的反射,因此你的问题甚至永远不会发生。
【解决方案3】:

不要在头文件中使用#include "dlist.cpp",而是将函数定义移动到dlist.h

【讨论】:

    【解决方案4】:

    为什么在 .h 文件中包含 .cpp 文件?在 99% 的情况下,您不应该这样做。

    只需添加您的代码

    template <typename T>
    bool Dlist<T>::isEmpty() const
    {
        return !first and !last;
    }
    

    而不是 .cpp 文件包含指令。

    【讨论】:

    • 头文件是为我定义的,我不允许以任何方式更改它。
    【解决方案5】:

    删除#include "dlist.h",不编译dlist.cpp本身。

    你也可以使用this

    因为 dlist.h 包含 dlist.cpp 并定义了__DLIST_H__

    #define __DLIST_H__
    

    你可以修改dlist.cpp为

    #ifdef __DLIST_H__
    
    template <typename T>
    bool Dlist<T>::isEmpty() const
    {
        return !first and !last;
    }
    
    #endif
    

    这样,如果您尝试编译 dlist.cpp,就不会出现编译器错误。 但我同意其他答案,最好不要将此文件命名为 .cpp。

    【讨论】:

    • “不编译 dlist.cpp 本身”是什么意思?
    • 我不知道您的环境,但例如不要将 .cpp 添加到您的项目中。
    • 这似乎没有任何意义。
    • @LightnessRacesinOrbit 为什么不呢,.cpp 包含在 .h 中,因此编译器将其视为头文件。
    • @Wimmel:是的,好吧,OP 场景的荒谬性暂时隐藏了这一点。
    猜你喜欢
    • 2012-10-13
    • 2015-09-15
    • 1970-01-01
    • 2012-11-08
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多