C++ 模板类型检查

C++ Template type cheking

本文关键字:检查 类型 C++      更新时间:2023-10-16
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;
            }

即使这不会被执行,因为fieldTypeSUPERFICIE的类型不同,这在编译时是未知的。基本上,您正在比较fieldType(这是一个const char*(与SUPERFICIE(这是一个long(,并且没有隐式比较器函数来进行该转换,因此编译器会抛出错误。

你可以做什么:

  • 专注于longconst 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的技术称为完美转发。这用于以通用方式移动语义或复制语义。您可以检查什么是转发引用或通用引用

有趣的部分是使用指向成员的指针。指向成员的指针的作用类似于类成员的别名。换句话说,您可以使用别名,而不是直接使用类的成员;可以使用指向成员的指针。

与特定实例一起使用时,指向成员的指针的作用就像访问成员所指向的成员一样。它们与操作员.*或操作员->*一起使用。运算符的左手是对象,右手是指向成员的指针。

在我的示例中,CONTINENTPAYS 和其他人指向关联的成员。

您可以在此处阅读有关 poiter 会员的更多信息:Pointers_to_data_members和此处:operator_member_access