【问题标题】:The strange behaviour when using c++ specialization template使用 c++ 特化模板时的奇怪行为
【发布时间】:2014-01-31 01:02:45
【问题描述】:

我是使用 c++ 模板编程的新手。我有 3 个代码文件

main.cpp

#include "template_test.h"
#include <iostream>

using namespace std;

int main()
{
    mytest<int> mt;
    mt.method(1);

    system("pause");

    return 0;
}

template_test.h

#include <iostream>
using namespace std;

template<class T>
class mytest
{
public:

    void method(T input){}
};


template<>
void mytest<int>::method(int input)
{
    cout << "ok" << endl;
}

template_test.cpp

#include "template_test.h"

//empty

代码在VS2013中生效。但是,当我将代码更改为 2 种情况时,我的代码都有问题。

1.第一个是链接器错误码。

main.cpp

#include "template_test.h"
#include <iostream>

using namespace std;

int main()
{
    mytest<int> mt;
    mt.method(1);

    system("pause");

    return 0;
}

template_test.h

#include <iostream>
using namespace std;

template<class T>
class mytest
{
public:

    void method(T input);
};

template<class T>
void mytest<T>::method(T input)
{
    cout << " " << endl;
}//explicit specialization here

template<>
void mytest<int>::method(int input)
{
    cout << "ok" << endl;
}

template_test.cpp

#include "template_test.h"

//empty

2.第二个输出什么都没有,而不是正确答案'ok'。

main.cpp

#include "template_test.h"
#include <iostream>

using namespace std;

int main()
{
    mytest<int> mt;
    mt.method(1);

    system("pause");

    return 0;
}

template_test.h

#include <iostream>
using namespace std;

template<class T>
class mytest
{
public:

    void method(T input){}
};

template_test.cpp

#include "template_test.h"

template<>
void mytest<int>::method(int input)
{
    cout << "ok" << endl;
}//move from .h to .cpp file here

c++ 模板的奇怪行为让我很困惑。那么,有什么问题呢?

【问题讨论】:

  • 这里回答了第二个问题:stackoverflow.com/questions/15061774/… 第一个问题,你得到的链接器错误是什么?
  • @jogojapan VS2013 报告 LNK2005 和 LNK1169 代码
  • 为什么你有一个 template_test.cpp 只包含 template_test.h (因此给该翻译单元它自己的模板函数副本)?您是否尝试过简单地删除该文件?
  • @cHao template_test.cpp只是第二种情况的例子。我将一些代码从 .h 文件移动到 .cpp 文件。对于前一种情况是没用的。
  • @jogojapan 啊,你是对的。

标签: c++ templates


【解决方案1】:

第一个问题是由你的明确专业化造成的

template<>
void mytest<int>::method(int input)
{
    cout << "ok" << endl;
}

在类定义之外的头文件and中定义and,没有关键字inline

显式特化会导致定义一个实际的函数(而不仅仅是一个模板)。该定义将出现在每个翻译单元中,因此如果您分别编译template_test.cppmain.cpp,则该函数的定义将包含在两个目标文件中,导致链接时出现多定义错误(因为它违反了ODR,单一定义规则)。

您最好通过在类模板定义中包含函数定义(通过将整个类模板专门用于int)或使用关键字inline 来避免这种情况:

template<>
inline void mytest<int>::method(int input)
{
    cout << "ok" << endl;
}

第二个问题是由于模板特化必须在使用前声明:

(14.7.3/6) 如果模板、成员模板或类模板的成员被显式特化,则应在第一次使用该特化之前声明该特化,这将导致发生隐式实例化,在发生这种使用的每个翻译单元中;不需要诊断。如果程序没有为显式特化提供定义,并且该特化的使用方式会导致发生隐式实例化,或者该成员是虚拟成员函数,则该程序是格式错误的,不需要诊断。对于已声明但未定义的显式特化,永远不会生成隐式实例化。 [...]

由于您的 main.cpp 包含头文件,但不包含 .cpp 文件,因此在 main.cpp 中使用模板特化的声明时未知。您可以通过在头文件中包含特化来最好地解决这个问题,或者在类模板定义中(通过将整个类模板特化为int),或者在类模板定义之外使用关键字inline

【讨论】:

    【解决方案2】:

    你先问第二个问题。使用模板时记住一条规则。不要将模板实现分离到 cpp 文件中。将它们全部放在一个 .h 文件中。

    你的第一个代码在我的 VC2012 中编译得很好,我不确定 VC2013 有什么问题。

    【讨论】:

    • 2012 显然不像 2013 年那样严格地定义多重定义,我猜。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 1970-01-01
    • 2020-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多