2012-07-04 23 views
1

如果我想创建一个模板类,并根据模板参数的typeid执行不同的操作,那么我该如何编码?如何针对模板类中的不同模板参数执行不同的操作?

例如,我有以下模板类,其中我想根据它是一个int还是一个字符串来初始化成员字段数据。

#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; 
} 

的错误是:错误C2440: '=':不能从 '为const char [2]' 到 'INT',这意味着代码转换实际上是检查其他-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语句可能会从模板中删除权力。有没有更好的方法来编码?

编辑:

#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; 
} 

链接器错误我现在得到的是:与托斯滕(如下图),我现在有以下阿文件的讨论后测试模板4.obj:错误LNK2019:函数_wmain中引用的无法解析的外部符号“public:__thiscall A ::〜A(void)”(?? 1?$ A @ H @@ QAE @ XZ)

+1

1)不要使用'_tmain'废话和朋友。 2)在模板中使用'typeid'有什么意义?因为没有'int :: operator =(const char *)',所以代码根本不会编译为'T = int' ... 3)请编译错误的测试用例。 – Griwes

+1

作为第二个例子的模板专门化是要走的方式恕我直言......你是否在你的头文件中加入了包容守卫?从链接器错误,它看起来像你没有。 – Naveen

+0

我想你可以使用SFINAE。有了'enable_if'和'is_same',你可以有不同的基于模板参数的成员funcs。 – jrok

回答

3

显式特化是要走的路。

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

专业化是定义,并且A :: A()和A :: A()只能有一个定义,所以它们只能在一个.cpp中定义。

你必须在阿移动明确的专业化一个.cpp

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

,并保持一个声明为他们

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(); 
} 
+0

感谢您的回答。尽管如此,我还是不明白。你说你把声明放在头文件中,但是你把它放在类中吗?如果我把它放在类中,编译器会说:错误C2931:'A ':template-class-id被重新定义为'A '的成员函数。如果我把它放在类的外面,链接器会说:error LNK2005:“public:__thiscall A :: A (int,int)”(?? 0?$ A @ H @@ QAE @ HH @ Z)已经定义在A.obj – physicalattraction

+0

@physicalattraction,我已经更新了一个完整的例子。 – AProgrammer

+0

这很有效,很清楚,正是我所期待的! :-D – physicalattraction

2

在情况下,它只是要具有行为依赖于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()) 
{ 
} 

亲切的问候 托斯滕

+0

感谢您的回答。现在我在头文件中包含上面的代码(包括结构之后的分号)。我把它放在类定义之后。与上述主要我仍然得到链接器错误LNK 2019.这个代码是否适合你?我有正确的订单吗? – physicalattraction

+0

@physicalattraction如果您的订单有误,编译器会抱怨。 LNK2019是一个链接器错误,抱怨缺少外部符号。也许你可以给我们提供一个非常小的例子来重现问题,包括错误信息的准确措辞。 –

+0

我已经编辑了开放职位的代码,因为我现在拥有它。我没有看到任何错误。也许你可以看到它? – physicalattraction

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() {} 
}; 
+0

谢谢你的回答。关于你的第一个解决方案:我如何在专门的类上调用泛型方法?更具体地说:我在泛型类中添加了一个GetData()方法,并在main中输入:A number(); cout << A.GetData();编译器然后给出错误C2039:'GetData'不是'A '的成员。我该如何解决这个问题? 关于你的第二个解决方案:那非常优雅。然而,我的真正问题有点复杂,通过根据输入类型调用方法来初始化字段数据。这就是为什么我想使用模板专业化。 – physicalattraction

+1

当使用带有类的模板时,编译器为每个专业化生成一个完全不同的类。通用模板类和专用模板类不像基类和派生类。因此,除非使用第二种解决方案,否则需要重新实现每个专业化中的GetData()方法。 – Sdra

相关问题