如何根据参数化类型的特定成员的类型对模板类函数进行不同的实现

How to have different implementation of a template class function based on the type of a specific member of the parameterized type

本文关键字:类函数 实现 类型 参数化类型 何根 成员      更新时间:2023-10-16

所以这是简化的问题,假设我们有两种类型

struct Shape2D
{
    Vec2 Position;
};
struct Shape3D
{
    Vec3 Position;
};

我想创建一个模板类测试

template<class T>
class Test
{
  public:
       int method1()
       {
          //return 1 if member T::Position is Vec2,
          //return 2 if member T::Position is Vec3
       }
}

以便以下代码有效

Test<A> ta;
Test<B> tb; 
assert(ta.method1() == 1);
assert(tb.method1() == 2);

此问题的真实上下文是针对OpenGL引擎的。我希望能够对 2D 和 3D 顶点数据使用相同的序列化类,而无需编写 3D 和 2D 版本。

有几种方法可以解决这个问题。

最简单的方法是只使用正常的重载分辨率:

template<class T>
class Test
{
private:
    T myT;
    int internal(Vec2)
    {
        return 1;
    }
    int internal(Vec3)
    {
        return 2;
    }
public:
    Test() : myT{} {}
    int method1()
    {
        return internal(myT.Position);
    }
};

这要求您实际具有 T 的实例。 如果没有,则需要使用基于模板的方法。 这是一个相当深入的主题,但是在您的示例中可以执行您想要的一种方法是:

template <typename T>
int internal();
template <>
int internal<Vec2>()
{
    return 1;
}
template <>
int internal<Vec3>()
{
    return 2;
}
template<class T>
class Test
{
public:
    int method1()
    {
        return internal<decltype(T::Position)>();
    }
};

您可以使用几个函数声明(在这种情况下不需要定义(,std::declvalstd::integral_constant在编译时解决它。
它遵循一个最小的工作示例:

#include<type_traits>
#include<utility>
struct Vec2 {};
struct Vec3{};
struct Shape2D { Vec2 Position; };
struct Shape3D { Vec3 Position; };
template<class T>
class Test {
    static constexpr std::integral_constant<int, 1> method1(Vec2);
    static constexpr std::integral_constant<int, 2> method1(Vec3);
public:
    constexpr int method1() {
        return decltype(method1(std::declval<T>().Position))::value;
    }
};
int main() {
    Test<Shape2D> ta;
    Test<Shape3D> tb; 
    static_assert(ta.method1() == 1, "!");
    static_assert(tb.method1() == 2, "!");
}

如果T没有名为 Position 的数据成员,则上述解决方案无法编译,其类型为 Vec2Vec3


另一种需要默认值的可能方法是:

constexpr int method1() {
    return
        (std::is_same<decltype(std::declval<T>().Position), Vec2>::value
        ? 1 : (std::is_same<decltype(std::declval<T>().Position), Vec3>::value
        ? 2 : 0));
}

那是与std::is_same结合使用的三元运算符,仅此而已。


如果可以使用 C++17,则还可以将解决方案基于if/else constexpr

constexpr int method1() {
    if constexpr(std::is_same_v<decltype(std::declval<T>().Position), Vec2>) {
        return 1;
    } else if constexpr(std::is_same_v<decltype(std::declval<T>().Position), Vec3>) {
        return 2;
    }
}
<</div> div class="answers">你可以

有一些oveload函数来运行不同的代码,比如你有int func (shape 2D x)int func (shape3D x),所以你只需调用func (T.position) method1,编译器就会帮助你解决调用。