【问题标题】:template class with functions that do not use the template具有不使用模板的功能的模板类
【发布时间】:2015-11-11 18:21:27
【问题描述】:

为了减少继承,我正在跳槽。

我读过一个类似的问题here。它显示了如何使用基类解决问题。我尝试放开继承,所以我正在寻找不同的东西——更多地沿着注释线。

我创建并编译了一个具有一种专业化的模板类 (normal)。需要模板的方法在标头(Mixer.hpp)中。不需要模板的方法在cpp文件中(Mixer.cpp)。当编译成静态库时,cpp 部分仅被编译为一种特化 (Mixer<normal>)。编译器当时不知道 (awsome)。将生成的静态库导入另一个项目并尝试创建不同的泛型 (awsome) 类会导致链接器错误,因为显然该库不包含该方法标识符 (Mixer<awesome>::noTemplateInvolved)。然而,正常实现的代码与任何代码一样好,因此链接器实际上可以链接到其他模板版本的现有源代码 (Mixer<?dontcare?>::noTemplateInvolved)。编译器所要做的就是为链接器适当地标记它。

这是导致链接器错误的源代码:

//Target compiled to Provider.lib
//Mixer.hpp
#pragma once
#include <iostream>
using namespace std;
struct normal { static void log() { cout << "normal\n"; } };

template<typename output = normal>
class Mixer
{
public:
    void callingTemplate();
    void noTemplateInvolved();
};

template<typename output>
void Mixer<output>::callingTemplate() { output::log(); }
//Mixer.cpp
#include "Mixer.hpp"
void Mixer<>::noTemplateInvolved()
{
    cout << "noTemplateInvolved\n";
}

//Target Compiled to User.exe
//This target imports Provider.lib
#include <Provider\Mixer.hpp>
#pragma comment(lib, "Provider.lib")

struct awsome { static void log() { cout << "awsome\n"; } };

int main()
{
    Mixer<> n;
    n.callingTemplate();
    n.noTemplateInvolved();

    Mixer<awsome> a;
    a.callingTemplate();
    a.noTemplateInvolved(); //linker error here
    return 0;
}

Mixer&lt;awsome&gt; 可以链接到方法callingTemplate,因为它的定义在头文件中并且编译器会创建该函数。在User.exe 编译时,noTemplateInvolved 的定义对编译器是隐藏的。编译器无法创建该方法并且链接必须失败。

我知道三种解决方案。

  • noTemplateInvolved的定义移到标题中。
  • 包含 cpp 文件
  • 从基类继承

我正在寻找另一种解决方案。 noTemplateInvolved 的正文确实与模板无关。我想在标题中注释方法。我希望编译器知道无论模板如何,它都应该始终使用相同的基本实现。

这可能吗?

编辑:在开头稍微注释了那个无聊的段落。

【问题讨论】:

  • 如果它与模板无关,为什么它是一个成员,而且是非静态的?您可以将它实现为一个单独的、非模板的非成员函数,并且可能只有一个成员存根转发给它——您必须提供存根的定义,但这只是很可能的一行内联。这是你要找的东西吗?无论如何,我不确定您为什么不想选择非依赖基础解决方案;也许在问题中解释这个决定会更清楚地说明您的要求。
  • 您可以使用显式模板实例化。为什么你不想移动非模板函数到基类?在这种情况下,它没有开销。
  • 最终目标是重构。如果我可以在标题中注释某些内容,我就不必触及实现。如果不出意外,这对我来说是有教育意义的。显式实例化在所呈现的情况下不起作用 - 在已知“struct awsome”之前编译库。尽管我认为它已被弃用,但我会尝试使用 extern 关键字。
  • 在我的实际生产代码中,我转而使用基类,但是我确实有另一个想法,我将很快实现,看看这是否能让我更接近注释表单。

标签: c++ templates static-libraries


【解决方案1】:

答案原来是 cmets 中建议的基类。我想要一个基类的原因之一是我不想重构。使用基类进行重构实际上非常简单。

original 类重命名为original_base。 从original_template继承继承自original_base。确保复制构造函数并将所有参数传递给基类。 声明 using original = original_template&lt;your default case here&gt; 确保目前无需修改其他源代码。

应用于上面的示例,我最终做了这样的事情:

//Target compiled to Provider.lib
//Mixer.hpp
#pragma once
#include <iostream>
using namespace std;
struct normal { static void log() { cout << "normal\n"; } };

class Mixer_Base
{
private:
    int mixcount;
public:
    Mixer_Base(int count);
    void noTemplateInvolved();
};

template<typename output = normal>
class Mixer_tempalte : public Mixer_Base
{
public:
    Mixer_tempalte(int count) : Mixer_Base(count)
    {}
    void callingTemplate();
};

template<typename output>
void Mixer_tempalte<output>::callingTemplate()
{
    output::log();
}

using Mixer = Mixer_tempalte<>;
//Mixer.cpp
#include "Mixer.hpp"

void Mixer_Base::noTemplateInvolved()
{
    cout << "noTemplateInvolved\n";
}

Mixer_Base::Mixer_Base(int count) : mixcount(count)
{}

//Target Compiled to User.exe
//This target imports Provider.lib
#include <Provider\Mixer.hpp>
#pragma comment(lib, "Provider.lib")

struct awsome { static void log() { cout << "awsome\n"; } };

int main()
{
    Mixer n(4);
    n.callingTemplate();
    n.noTemplateInvolved();

    Mixer_tempalte<awsome> a(3);
    a.callingTemplate();
    a.noTemplateInvolved();
    return 0;
}

在某种程度上,注释的感觉就像基类的感觉。基类中的所有内容现在都按照我想要的方式进行了注释,尽管这不会减少继承。

【讨论】:

    猜你喜欢
    • 2011-10-24
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多