【问题标题】:How can I perform different actions for different template parameters in a template class?如何对模板类中的不同模板参数执行不同的操作?
【发布时间】:2012-07-04 17:52:26
【问题描述】:

如果我想做一个模板类,并且根据模板参数的typeid执行不同的动作,那么我该如何编码呢?​​

例如,我有以下模板类,我想在其中初始化成员字段数据,具体取决于它是 int 还是 string。

#include <string>

template <class T>
class A
{
private:
    T data;
public:
    A();
};

// Implementation of constructor
template <class T>
A<T>::A()
{
    if (typeid(T) == typeid(int))
    {
        data = 1;
    }
    else if (typeid(T) == typeid(std::string))
    {
        data = "one";
    }
    else
    {
        throw runtime_error("Choose type int or string");
    }
}

但是,此代码无法通过以下主文件编译。

#include "stdafx.h"
#include "A.h"
#include <string>

int _tmain(int argc, _TCHAR* argv[])
{
    A<int> one;
    return 0;
}

错误是:error C2440: '=' : cannot convert from 'const char [2]' to 'int',这意味着代码实际上正在检查 else-if 语句中的 int,即使它永远不会能够到达代码的那部分。

接下来,按照这个例子 (Perform different methods based on template variable type),我尝试了下面的 A.h 文件,但是我收到了几个链接器错误,提到 A(void) 已经在 A.obj 中定义。

#include <string>

template <class T>
class A
{
private:
    T data;
public:
    A();
    ~A();
};

// Implementation of constructor
template <>
A<int>::A()
{
    data = 1;
}
template <>
A<std::string>::A()
{
    data = "one";
}

有人知道如何启动并运行此代码吗?我也意识到在模板类中使用这样的 if-else 语句可能会削弱模板的力量。有没有更好的编码方式?

编辑:在与 Torsten(下)讨论后,我现在有以下 A.h 文件:

#pragma once

#include <string>

// Class definition
template <class T>
class A
{
public:
    A();
    ~A();
private:
    T data;
};

// Implementation of initialization
template < class T > 
struct initial_data
{
  static T data() { throw runtime_error("Choose type int or string"); }
};

template <> 
struct initial_data< int >
{
    static int data() { return 1; }
};

template <> 
struct initial_data< std::string >
{
    static std::string data() { return "one"; }
};

// Definition of constructor
template <class T>
A<T>::A()
  : data( initial_data< T >::data() ) 
{
}

以及以下主要内容:

#include "stdafx.h"
#include "A.h"
#include <string>

int _tmain(int argc, _TCHAR* argv[])
{
    A<int> ione;

    return 0;
}

我现在得到的链接器错误是:Test template 4.obj : error LNK2019: unresolved external symbol "public: __thiscall A::~A(void)" (??1?$A@H@@QAE@XZ ) 在函数 _wmain 中引用

【问题讨论】:

  • 1) 不要使用_tmain 废话和朋友。 2) 将typeid 与模板一起使用有什么意义?代码根本不会为T = int 编译,因为没有int::operator=(const char *)... 3) 请测试该编译错误的用例。
  • 您在第二个示例中所做的模板专业化是恕我直言的方法。您是否在头文件中添加了包含保护?从链接器错误看来你没有。
  • 我认为您可以为此使用 SFINAE。使用enable_ifis_same,您可以根据模板参数拥有不同的成员函数。
  • @jrok:我没听说过 SFINAE,但我会去看看。
  • @Naveen:我的所有头文件中都有包含保护,所以这不是原因。

标签: c++ templates typeid


【解决方案1】:

明确的专业化是要走的路。

我假设您将 A.h 包含在多个 .cpp 中,这就是问题的根本原因。

专业化是定义,A::A() 和 A::A() 必须只有一个定义,因此它们只能在一个 .cpp 中。

您必须在 .cpp 中移动显式特化

template <>
A<int>::A()
{
    data = 1;
}
template <>
A<std::string>::A()
{
    data = "one";
}

并在 A.h 中为他们保留一份声明

template<> A<int>::A();
template<> A<std::string>::A();

以便编译器知道它们是明确专门化的,并且不会尝试添加自动的。

编辑:使用这四个文件,g++ m.cpp f.cpp a.cpp 不会显示任何错误。

// a.h
#define A_H

#include <string>

template <class T>
class A
{
private:
    T data;
public:
    A();
};

template<> A<int>::A();
template<> A<std::string>::A();

#endif

// a.cpp
#include "a.h"

template <>
A<int>::A()
{
    data = 1;
}
template <>
A<std::string>::A()
{
    data = "one";
}

// f.cpp
#include "a.h"

int f()
{
    A<int> one;
    A<std::string> two;
}

// m.cpp
#include "a.h"

int f();

int main()
{
    A<int> one;
    A<std::string> two;
    f();
}

【讨论】:

  • 感谢您的回答。我还没有让它工作。您说您将声明放在头文件中,但是您将其放在类中吗?如果我将它放在类中,编译器会说:错误 C2931: 'A' : template-class-id redefined as a member function of 'A'。如果我把它放在课堂之外,链接器会说: error LNK2005: "public: __thiscall A::A(int,int)" (??0?$A@H@@QAE@HH@ Z) 已经在 A.obj 中定义
  • 这很有效,很清楚,正是我要找的! :-D
【解决方案2】:

您在第二种解决方案中是正确的,您需要的是模板专业化(将声明和实现保持在一起):

#include <string>

template <class T>
class A
{
private:
    T data;
public:
    A();
    ~A();
};

template <>
class A <std::string>
{
private:
  std::string data;
public:
  A() { data = "one"; }
};

template <>
class A <int>
{
private:
  int data;
public:
  A() { data = 1; }
};

如果我可以建议一个更优雅的解决方案,那么我会在构造函数中添加一个参数并避免模板专业化:

template <class T>
class A
{
private:
    T data;
public:
    A( T value ) : data( value ) {}
    virtual ~A() {}
};

【讨论】:

  • 感谢您的回答。关于您的第一个解决方案:如何在专用类上调用泛型方法?更具体地说:我在泛型类中添加了一个 GetData() 方法并输入 main: A number(); cout ' 的成员。我该如何解决这个问题?关于您的第二个解决方案:这非常优雅。我真正的问题有点复杂,字段数据是通过根据输入类型调用方法来初始化的。这就是我想使用模板专业化的原因。
  • 当使用带有类的模板时,编译器会为每个特化生成一个完全不同的类。通用模板类和专用模板类不同于基类和派生类。因此,除非您使用第二种解决方案,否则您需要在每个特化中重新实现 GetData() 方法。
【解决方案3】:

如果它只是你想要的行为依赖于 T 的 c'tor,我建议将其分解为不同的结构:

template < class T > 
struct initial_data
{
  static T data() { throw runtime_error("Choose type int or string"); }
};

template <> 
struct initial_data< int >
{
    static int data() { return 1; }
}

template <> 
struct initial_data< std::string >
{
    static std::string data() { return "1"; }
}

如果你对一个类的模板参数进行特化,不同的特化是完全不同的类型,可以有不同的数据和函数集。

最后:

template <class T>
A<T>::A()
  : data( initial_data< T >::data() ) 
{
}

亲切的问候 托斯滕

【讨论】:

  • 感谢您的回答。我现在在头文件中有上面的代码(包括结构后的分号)。我把它放在类定义之后。使用上述主要内容,我仍然会收到链接器错误 LNK 2019。此代码对您有用吗?我的订单对吗?
  • @physicalattraction 如果您的顺序错误,编译器会抱怨。 LNK2019 是一个链接器错误,抱怨缺少外部符号。也许您可以为我们提供一个非常小的示例来重现问题,包括错误消息的确切措辞。
  • 我已经用我现在拥有的代码编辑了开头的帖子。我看不出有什么错误。也许你能看到它?
  • @physicalattraction 您为 A 声明了一个析构函数,但没有定义一个。这就是链接器所抱怨的。添加析构函数的定义或删除声明:-)
  • 行得通!我现在可以使用您和 AProgrammer 的解决方案运行完整的代码。
猜你喜欢
  • 2020-12-15
  • 1970-01-01
  • 2020-03-26
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 2011-12-14
  • 1970-01-01
相关资源
最近更新 更多