0

在我的碰撞/物理引擎中,我知道我在运行时使用的空间分区方法,这要归功于模板。我正在尝试创建一个通用Query类,它允许我对任何空间分区方法执行相同的查询。每个空间分区方法都实现了自己的查询版本,但接口是相同的。

问题是用户必须指定空间分区类型、空间分区类型的查询类型和空间分区类型查询模式

如果用户改变其空间划分方法,所有现有代码都会中断。

有没有办法让用户只指定空间分区类型、通用查询类型和通用查询模式并具有某种别名以便自动选择正确的类型(相对于空间分区类型)?

我已经尝试过using(C++11 typedef) 以及decltype部分专业化,但是我找不到编写代码的正确方法,而且我从来没有让它编译/工作。

我只能考虑使用virtual方法和基类——但这似乎没有必要,因为我在编译时“知道”类型。


当前代码:

 // Query.h
 template<class T, class U, class V>
 Query<T, U, V> getQuery() { return Query<T, U, V>(getSpatial<T>()); } 

 // Example.cpp (user project)
 getQuery<Grid, GridQueryType::Point, GridQueryMode::All>(); 
 getQuery<QuadTree, QuadTreeQueryType::Point, QuadTreeQueryMode::All>();

所需代码(无效):

 // Query.h
 namespace Type { struct Point; }
 namespace Mode { struct All; }
 template<class T, class U, class V>
 Query<T, typename T::U, typename T::V> getQuery() 
 { 
      return Query<T, typename T::U, typename T::V>(getSpatial<T>()); 
 } 

 // Grid.h
 using Type::Point = GridQueryType::Point;
 using Mode::All = GridQueryMode::All;

 // QuadTree.h
 using Type::Point = QuadTreeQueryType::Point;
 using Mode::All = QuadTreeQueryMode::All;

 // Example.cpp (user project)
 getQuery<Grid, Type::Point, Mode::All>(); // actually uses GridQueryType::Point and GridQueryMode::All!
 getQuery<QuadTree, Type::Point, Mode::All>(); // actually uses QuadTreeQueryType::Point and QuadTreeQueryMode::All!

实际代码也传递typename... TArgs以允许特定于查询类型的参数 - 由于空间原因,我将其从示例中排除。

4

3 回答 3

2

如果您的第一个模板参数与第二个和第三个模板参数之间存在直接关系,则似乎后者不应被视为模板参数,而应由类型映射确定:

template <typename> struct TypePoint;
template <typename> struct ModeAll

template <> struct TypePoint<Grid> { typedef GridQueryType::Point type; };
template <> struct ModeAll<Grid>   { typedef GridQueryMode::All type; };

getQuery<Grid>();

至少,使用上面的类型映射,您可以为参数定义默认值。如果要遵循其他参数,您基本上可以将三个第一个参数及其默认值组合成一个类型,而不是传递,例如:

template <typename T,
          typename P = typename TypePoint<T>::type,
          typename M = typename ModeAll<T>::type>
struct QueryParameters {
    typedef T type;
    typedef P point_type;
    typedef M mode_type;
};
getQuery<QueryParemeters<Grid>, Other, Paremters>();
于 2013-08-21T10:15:07.960 回答
1

I think the solution here is a combination of template specialization and template template parameters.

Firts we define the different types of query:

namespace Type
{
    template<typename SPATIAL_PARTITION>
    struct Point;
}

namespace Mode
{
    template<typename SPATIAL_PARTITION>
    struct All;
}

Next, in each spatial partitionning implementation, we specialize the types and modes. For example, in Quadtree.h:

namespace Type
{
    template<>
    struct Point<Quadtree>
    {
        /* ... implementation here ... */
    }
}

namespace Mode
{
    template<>
    struct All<Quadtree>
    {
        /* ... implementation here ... */
    }
}

Finally, in the query we use template template parameters to specify the mode and type:

template<typename SPATIAL_PARTITION, template<typename> class TYPE , template<typename> class MODE>
void getQuery()
{
    using type = TYPE<SPATIAL_PARTITION>;
    using mode = MODE<SPATIAL_PARTITION>;
}
于 2013-08-21T10:18:32.373 回答
0

我会朝另一个方向走。由于您有模板功能,您可以使用类型推导。返回对象取决于函数模板参数。因此,您可以 typedef 返回类型而不是别名。

// Grid.h
//using Type::Point = GridQueryType::Point;
//    using Mode::All = GridQueryMode::All;
typedef Query<Grid, GridQueryType::Point, GridQueryMode::All> T_one;

// QuadTree.h
//using Type::Point = QuadTreeQueryType::Point;
//using Mode::All = QuadTreeQueryMode::All;

typedef Query<Grid, QuadTreeQueryType::Point, QuadTreeQueryMode::All> T_two;

// Example.cpp (user project)
//getQuery<Grid, Type::Point, Mode::All>(); // actually uses GridQueryType::Point and     
//getQuery<QuadTree, Type::Point, Mode::All>();
T_one t = getQuery();
T_two t2 = getQuery();
于 2013-08-21T10:23:44.877 回答