在.cpp文件中定义模板函数(出现错误)

3

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 文件中定义函数?谢谢。

头文件中有一个 } 括号,它不应该在那里。我猜是打错了? - wimh
在 .cpp 文件中定义模板函数,不要这样做! - Lightness Races in Orbit
5个回答

7
你必须将类模板的成员函数实现放在头文件中,或者放在被头文件包含的文件中。编译器需要访问这些代码来为任何给定的类型实例化模板。
在你的情况下,问题似乎是你将头文件包含在了.cpp文件中,反之亦然。如果你真的想将声明和实现分开放在不同的文件中,我建议将实现的后缀从.cpp改为其他名称,例如.icpp。一些构建系统可能会尝试将任何带有.cpp后缀的文件编译成对象文件,这也会导致错误。
步骤如下:
1.从dlist.cpp中删除#include "dlist.h"。 2.(可选)将dlist.cpp重命名为类似于dlist.icpp的名称。为什么?因为许多构建系统会自动将以.cpp结尾的文件编译为对象文件。并且许多程序员假设.cpp文件将被编译为对象文件。 3.(只有在执行步骤2时)将重命名的dlist.icpp包含在dlist.h中,就像当前对dlis.cpp所做的那样。

1
@BobJohn从你的.cpp文件中删除#include "dlist.h"。也许将.cpp的后缀更改,并在你的.h文件中包含它(就像你现在所做的那样)。 - juanchopanza
1
我不被允许更改头文件。有人给了你一个类模板的头文件,但没有为其实例化的成员函数提供定义,并禁止你对其进行更改?今天就辞职吧。 - Lightness Races in Orbit
哦,它包含了一个 .cpp 文件。天哪! - Lightness Races in Orbit
1
@BobJohn:他的意思是不要将其作为一个独立的单元进行编译——它的代码已经随着递归地#include它作为头文件的任何代码一起编译了,不应该单独构建。 - Lightness Races in Orbit
因此,它不会像其他.cpp文件一样运作,也不应该被称为这个名字。 - Lightness Races in Orbit
显示剩余9条评论

2
“头文件已经为我定义好了,我不允许以任何方式进行更改。” 然后,您需要从 .cpp 中删除 #include "dlist.h" 指令(因为您已经在 dlist.h 中,并且因此创建了循环依赖),这会使一切完全倒置,因为您所获得的头文件非常愚蠢!.cpp 不应该被 #included。通常,如果必须将与模板相关的定义拆分到其自己的文件中,它应该具有其他扩展名。我强烈建议您与要求您这样做的人交谈,并解释他们的头文件是愚蠢的、令人困惑和非常不规范的。

以下是提供的注释:注意:以下行仅包含因为g++编译器需要查看您的方法的“模板化”版本。这是唯一可以#包含cpp文件的情况。 - Bob John
1
@BobJohn:嗯,笔记是错的。这是 可接受的。该文件应该有一个不同于 .cpp 的扩展名。 - Lightness Races in Orbit
有趣。你能详细说明一下为什么这是这样吗?我很想知道为什么它如此被广泛地不赞同。 - Bob John
@BobJohn:正因为这个原因。因为你在头文件中包含了.cpp,所以你需要小心,不要在.cpp中包含.hpp,并且配置(可能是_黑客_!)你的构建系统不要将.cpp编译为翻译单元。这是常规惯例的100%逆转,对于任何第一次维护你的代码的人来说都会感到惊讶。如果文件有像.ipp(或其他东西)这样的异常,我怀疑你甚至从未有过在第一次使用时就#include“dlist.h”的反应,因此你的问题甚至不会发生。 - Lightness Races in Orbit

1
为什么要在.h文件中包含.cpp文件?在99%的情况下,你不应该这样做。
只需添加您的代码即可。
template <typename T>
bool Dlist<T>::isEmpty() const
{
    return !first and !last;
}

使用#include指令替代.cpp文件的包含。


1
头文件已经为我定义好了,我不能以任何方式更改它。 - Bob John

1

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


0

移除 #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本身”是什么意思? - Bob John
我不知道你的环境,但是举个例子,不要将.cpp文件添加到你的项目中。 - wimh
那似乎毫无意义。 - Lightness Races in Orbit
@LightnessRacesinOrbit 为什么不行呢,.cpp 文件是从 .h 文件中包含的,因此编译器将其视为头文件。 - wimh
@Wimmel:没错,好的,原帖中荒谬的情节让我一时没注意到这点。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接