C++ 模板类型检查
C++ Template type cheking
class Nation{
private :
string continent;
string pays;
string capital;
long superficie;
long population;
public :
static const string CONTINENT ;
static const string PAYS ;
static const string CAPITAL ;
static const long SUPERFICIE;
static const long POPULATION;
.....
const string Nation::CONTINENT ="1";
const string Nation::PAYS ="2";
const string Nation::CAPITAL ="3";
const long Nation::SUPERFICIE= 4;
const long Nation::POPULATION= 5;
....
template <class T>
void Nation::setField(T fieldType, T valeur){
if(typeid(fieldType).name()==typeid(this->CAPITAL).name()){
if (fieldType==this->PAYS ) this->pays = valeur;
if (fieldType==this->CAPITAL ) this->capital = valeur;
if (fieldType==this->CONTINENT) this->continent = valeur;
}
else if(typeid(fieldType).name()==typeid(this->SUPERFICIE).name()){
if (fieldType==this->SUPERFICIE) this->superficie = valeur;
if (fieldType==this->POPULATION) this->population = valeur;
}
}
.......
}
编译器说有一些错误:"[Error] no match for 'operator==' (operand types are 'std::basic_string<char>' and 'const long int')"
在行:
if (fieldType==this->SUPERFICIE) this->superficie = valeur;
这是一个编译错误,而不是运行时。
调用函数:
country1.setField(Nation::PAYS, "CANADA");
country1.setField(Nation::POPULATION, 12345678);
我提前感谢您。
这就是模板的(问题(,它们在编译时被评估,因此像if(typeid(fieldType).name()==typeid(this->CAPITAL).name())
这样的条件不会"保护"模板的后续评估。
发生的情况是,当你打电话时
country1.setField(Nation::PAYS, "CANADA");
T
(在编译时(解析为const char*
,因此fieldType
的类型将是const char*
。然后,在这个分支中
else if(typeid(fieldType).name()==typeid(this->SUPERFICIE).name()){
if (fieldType==this->SUPERFICIE) this->superficie = valeur;
if (fieldType==this->POPULATION) this->population = valeur;
}
即使这不会被执行,因为fieldType
与SUPERFICIE
的类型不同,这在编译时是未知的。基本上,您正在比较fieldType
(这是一个const char*
(与SUPERFICIE
(这是一个long
(,并且没有隐式比较器函数来进行该转换,因此编译器会抛出错误。
你可以做什么:
- 专注于
long
和const char*
setField
- 使用 2 个重载函数(
const char*
和long
(代替模板
模板不能以这种方式工作,你不能突然将类型从 if "更改"到另一个。你可以做的是重载:
void Nation::setField(string fieldType, string valeur){
if (fieldType==this->PAYS ) this->pays = valeur;
if (fieldType==this->CAPITAL ) this->capital = valeur;
if (fieldType==this->CONTINENT) this->continent = valeur;
}
void Nation::setField(long fieldType, long valeur){
if (fieldType==this->SUPERFICIE) this->superficie = valeur;
if (fieldType==this->POPULATION) this->population = valeur;
}
一些进一步的评论:
- 将字符串函数参数作为常量参考
- 尝试使用
switch()
而不是if()
(在第二种情况下有效( - 根据您的使用案例,您可能希望单独将字符串转换为字段索引。如果您两次访问同一字段,您肯定希望这样做。
首先,我会将您的常量声明为枚举,并使函数的第一个参数成为该类型。 无需使指标与您尝试存储的字段的类型相同。
其次,正如其他人指出的那样,重载函数更适合您尝试使用的内容。 模板实际上在编译之前在后台创建重载函数,然后编译它们。 因此,您编写的内容实际上对编译器来说更像这样:
void Nation::setField(int fieldType, int valeur){
if(typeid(fieldType).name()==typeid(this->CAPITAL).name()){
if (fieldType==this->PAYS ) this->pays = valeur;
if (fieldType==this->CAPITAL ) this->capital = valeur;
if (fieldType==this->CONTINENT) this->continent = valeur;
}
else if(typeid(fieldType).name()==typeid(this->SUPERFICIE).name()){
if (fieldType==this->SUPERFICIE) this->superficie = valeur;
if (fieldType==this->POPULATION) this->population = valeur;
}
}
void Nation::setField(string fieldType, string valeur){
if(typeid(fieldType).name()==typeid(this->CAPITAL).name()){
if (fieldType==this->PAYS ) this->pays = valeur;
if (fieldType==this->CAPITAL ) this->capital = valeur;
if (fieldType==this->CONTINENT) this->continent = valeur;
}
else if(typeid(fieldType).name()==typeid(this->SUPERFICIE).name()){
if (fieldType==this->SUPERFICIE) this->superficie = valeur;
if (fieldType==this->POPULATION) this->population = valeur;
}
}
仔细观察,你可以看到编译器将如何抱怨。
因此,要使用枚举进一步修改您的代码,如下所示:
...
enum FieldType {CONTINENT, PAYS, CAPITAL, SUPERFICIE, POPULATION};
void Nation::setField(FieldType fieldType, string valeur){
if (fieldType==FieldType::PAYS) this->pays = valeur;
if (fieldType==FieldType::CAPITAL) this->capital = valeur;
if (fieldType==FieldType::CONTINENT)this->continent = valeur;
}
void Nation::setField(FieldType fieldType, long valeur){
if (fieldType==FieldType::SUPERFICIE) this->superficie = valeur;
if (fieldType==FieldType::POPULATION) this->population = valeur;
}
...
编译器将根据传递给 valeur 参数的值类型自动决定调用哪个函数。 从那里,您所要做的就是决定在哪里存储值。
元维基:由于其他解决方案已经给出了解决问题的方法,我认为从另一个角度看待这个问题对 OP 和其他稍后将要研究这个问题的人都是有益的。
如果你只关心调用方法的语义,你可以这样做,这不仅更快,而且更灵活:
struct Nation {
private :
string continent;
string pays;
string capital;
long superficie;
long population;
public :
static const constexpr auto CONTINENT = &Nation::continent;
static const constexpr auto PAYS = &Nation::pays;
static const constexpr auto CAPITAL = &Nation::capital;
static const constexpr auto SUPERFICIE = &Nation::superficie;
static const constexpr auto POPULATION = &Nation::population;
};
现在,实现setField
函数变得微不足道。您唯一需要的实现是:
template<typename Member, typename V>
void setField(Member m, V&& value) {
this->*m = std::forward<V>(value);
}
然后,你可以像这样调用你的函数:
Nation country1;
country1.setField(Nation::PAYS, "CANADA");
country1.setField(Nation::POPULATION, 12345678);
如果编译时反射可用,甚至可以自动生成指针。
编辑:这是对此处使用的概念的快速解释。
我用于&&
和std::forward
的技术称为完美转发。这用于以通用方式移动语义或复制语义。您可以检查什么是转发引用或通用引用。
有趣的部分是使用指向成员的指针。指向成员的指针的作用类似于类成员的别名。换句话说,您可以使用别名,而不是直接使用类的成员;可以使用指向成员的指针。
与特定实例一起使用时,指向成员的指针的作用就像访问成员所指向的成员一样。它们与操作员.*
或操作员->*
一起使用。运算符的左手是对象,右手是指向成员的指针。
在我的示例中,CONTINENT
、PAYS
和其他人指向关联的成员。
您可以在此处阅读有关 poiter 会员的更多信息:Pointers_to_data_members和此处:operator_member_access
- 键入特征以检查类型是否可从流和 MSVC 读取
- 在编译时检查类型是否为 std::basic_string<T> C++
- 检查类型 T 是否具有成员函数 SFINAE 的任何重载
- 检查类型是否为模板的实例化
- 如何检查类型 'T' 是否具有'T(std::initializer_list<U>)'构造函数
- 使用 python 绑定来检查 C++ 类型是否是规范方式的指针、引用等?
- 检查类型的相等性
- C++编译时检查类型
- 使用模板C++检查类型
- 如何检查类型 T 是否在参数包 Ts 中
- 如何使用条件来检查类型名 T 是否是 C++ 中浮点类型的整数类型
- 检查类型是否在 C++ 中定义了 [][]
- 是否有类型特征检查类型之间的包含
- 如何在运行时检查类型
- 使用SFINAE检查类型是否可以绑定到模板模板参数
- 如何检查类型是否是给定类模板的实例化
- 如何在编译时检查类型
- 检查类型是否可以作为 boost::lexical_cast <string>的参数
- 如何检查类型是通过 typedef 定义的还是在模板参数中使用定义的
- 检查类型是否为地图