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

时间:2017-06-10 03:10:23

标签: c++ templates compilation conditional member

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

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版本。

3 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:1)

您可以使用一些函数声明(在这种情况下不需要定义),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;
    }
}

答案 2 :(得分:0)

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