尝试根据模板类型重载模板类中的虚函数

问题描述

我今天有额外个有趣的恶作剧。

我对C ++中的模板有些陌生。这里是一些当前在我的代码中的类:

class location2d
{
    int x,y;
}

class location3d
{
    int x,y,z;
}

template <typename T>
class myClass : public parentClass<float,T>
{
    private:

    virtual void myFunction (T position) const override final
    {
        // some math stuff (this part doesn't matter)
        something = position.x + position.y;
    }

    int something;
};

现在,这已硬编码到location2d。如果传入location3d,我需要myFunction()具有不同的行为。例如:

virtual void myFunction (T position) const override final
{
    something = position.x + position.y + position.z;
}

我已经阅读了模板专业化知识,但是由于myFunction()在基类中覆盖了虚函数,所以这变得棘手。据我了解,我们无法专门研究虚拟功能。无论如何我都尝试过。它讨厌它。

我的第二个想法是键入检查模板,然后只调用单独的助手:

virtual void myFunction (T position) const override final
{
    if (std::is_same<T,location3d) {myFunction3(position);}
    else {myFunction2(position);}
}

void myFunction2 (T position) const
{
    something = position.x + position.y;
}

void myFunction3 (T position) const
{
    something = position.x + position.y + position.z;
}

这里的问题是编译器抛出“ location2d不包含成员'z'”,这是绝对正确的。但是,除非存在z,否则不会调用myFunction3()。

接下来,我尝试专门进行强制转换,以使T不再是模棱两可的:

virtual void myFunction (T position) const override final
{
    if (std::is_same<T,location3d>::value) {myFunction3((location3d)position);}
    else {myFunction2((location2d)position);}
}

”“类型转换”:无法从“ T”转换为“ location3””。

最终想法: 由于辅助功能不是虚拟的,也许我可以专门介绍这两个。

virtual void myFunction (T position) const override final
{
    if (std::is_same<T,location3d>::value) {mySecondFunction<location3d>(position);}
    else {mySecondFunction<location2d>(position);}
}

template<>
void mySecondFunction<location2d> (location2d position) const {}

template<>
void mySecondFunction<location3d> (location3d position) const {}

我不确定我是否做错了,但这引发了很多语法错误,我不知道如何解决

最终,我想要做的只是根据是否存在“ z”来改变myFunction()的行为,并且我不喜欢它的完成方式。我觉得这里一定缺少简单的东西。

解决方法

您进行类型检查的想法是正确的,但是您的方法需要更多的工作来帮助编译器。

如果您使用的是C ++ 17或更高版本,请将if constexprstd::is_same_v结合使用,例如:

template <typename T>
class myClass : public parentClass<float,T>
{
private:
    virtual void myFunction (T position) const override final
    {
        if constexpr (std::is_same_v<T,location3d>) {
            something = position.x + position.y + position.z;
        }
        else {
            something = position.x + position.y;
        }
    }

    int something;
};

编译器将在编译时完全评估if constexpr并消除最终运行时代码中未使用的分支,从而为myClass<T>的每个实例生成不同的代码,例如:

class myClass<location2d> : public parentClass<float,location2d>
{
private:
    virtual void myFunction (location2d position) const override final
    {
        something = position.x + position.y;
    }

    int something;
};

class myClass<location3d> : public parentClass<float,location3d>
{
private:
    virtual void myFunction (location3d position) const override final
    {
        something = position.x + position.y + position.z;
    }

    int something;
};

如果不适合使用C ++ 17或更高版本,则可以改用reinterpret_cast,例如:

template <typename T>
class myClass : public parentClass<float,T>
{
private:
    virtual void myFunction (T position) const override final
    {
        if (std::is_same<T,location3d>::value) {
            // if T is NOT location3d then accessing position.z as-is
            // will fail to compile if T::z is missing,hence the cast.
            // Since this branch is executed only when T is location3d,// the cast in this branch is redundant but harmless. But
            // this branch is still compiled even when T is NOT loction3d...
            something = position.x + position.y + reinterpret_cast<location3d&>(position).z;
        }
        else {
            // no cast is needed here since location2d and location3d
            // both have x and y fields...
            something = position.x + position.y;
        }
    }

    int something;
};

不进行强制转换,编译器将为myClass<T>的每个实例生成如下代码:

class myClass<location2d> : public parentClass<float,location2d>
{
private:
    virtual void myFunction (location2d position) const override final
    {
        if (false) {
            something = position.x + position.y + position.z; // ERROR! location2d::z does not exist...
        }
        else {
            something = position.x + position.y; // OK
        }
    }

    int something;
};

class myClass<location3d> : public parentClass<float,location3d>
{
private:
    virtual void myFunction (location3d position) const override final
    {
        if (true) {
            something = position.x + position.y + position.z; // OK
        }
        else {
            something = position.x + position.y; // OK
        }
    }

    int something;
};

当将position传递给非模板成员方法时,会发生相同的问题,例如:

template <typename T>
class myClass : public parentClass<float,location3d) {
            // if T is NOT location3d,passing position as-is to myFunction3()
            // would fail to compile,hence the cast. Since this branch is
            // executed only when T is location3d,the cast in this branch
            // is redundant but harmless. But this branch is still compiled
            // even when T is NOT loction3d...
            myFunction3(reinterpret_cast<location3d&>(position));
        }
        else {
            // if T is NOT location2d,passing position as-is to myFunction2()
            // would fail to compile,hence the cast. Since this branch is
            // executed only when T is location2d,the cast in this branch
            // is redundant but harmless. But this branch is still compiled
            // even when T is NOT location2d...
            myFunction2(reinterpret_cast<location2d>(position));
        }
    }

    void myFunction2 (location2d position)
    {
        something = position.x + position.y;
    }

    void myFunction3 (location3d position)
    {
        something = position.x + position.y + position.z;
    }

    int something;
};

在不进行强制转换的情况下,编译器将为myClass<T>的每个实例生成如下代码:

class myClass<location2d> : public parentClass<float,location2d>
{
private:
    virtual void myFunction (location2d position) const override final
    {
        if (false) {
            myFunction3(position); // ERROR! can't convert from location2d to location3d
        }
        else {
            myFunction2(position); // OK
        }
    }

    void myFunction2 (location2d position)
    {
        something = position.x + position.y;
    }

    void myFunction3 (location3d position)
    {
        something = position.x + position.y + position.z;
    }

    int something;
};

class myClass<location3d> : public parentClass<float,location3d>
{
private:
    virtual void myFunction (location3d position) const override final
    {
        if (true) {
            myFunction3(position); // OK
        }
        else {
            myFunction2(position); // ERROR! can't convert from location3d to location2d
        }
    }

    void myFunction2 (location2d position)
    {
        something = position.x + position.y;
    }

    void myFunction3 (location3d position)
    {
        something = position.x + position.y + position.z;
    }

    int something;
};

话虽这么说,另一种选择是使用模板专业化,那么根本就不需要时髦的转换,例如:

template<typename T>
int add_them_up(T) { return 0; }

template<>
int add_them_up<location2d>(location2d position)
{
    return position.x + position.y;
}

template<>
int add_them_up<location3d>(location3d position)
{
    return position.x + position.y + position.z;
}

template <typename T>
class myClass : public parentClass<float,T>
{
private:
    virtual void myFunction (T position) const override final
    {
        something = add_them_up<T>(position);
    }

    int something;
};

编译器将为myClass<T>的每个实例生成如下代码:

int add_them_up<location2d>(location2d position)
{
    return position.x + position.y;
}

int add_them_up<location3d>(location3d position)
{
    return position.x + position.y + position.z;
}

class myClass<location2d> : public parentClass<float,location2d>
{
private:
    virtual void myFunction (location2d position) const override final
    {
        something = add_them_up<location2d>(position);
    }

    int something;
};

class myClass<location3d> : public parentClass<float,location3d>
{
private:
    virtual void myFunction (location3d position) const override final
    {
        something = add_them_up<location3d>(position);
    }

    int something;
};

在编译器内联调用站点的专用功能后,非常熟悉 1

1: C ++ 17 if constexpr输出!

class myClass<location2d> : public parentClass<float,location3d>
{
private:
    virtual void myFunction (location3d position) const override final
    {
        something = position.x + position.y + position.z;
    }

    int something;
};