可变参数模板扩展期间的C ++索引类型

时间:2013-02-21 23:17:15

标签: c++ templates c++11 variadic-templates

我有一个简单而又令人生畏的问题,我自己无法解决。我有类似

的东西
template<class T, class... Args>
T* create(SomeCastableType* args, size_t numArgs)
{
  return new T(static_cast<Args>(args[INDEX_OF_EXPANSION])...);
}

假设SomeCastableType可以转换为任何类型。显然我无法得到的是INDEX_OF_EXPANSION

非常感谢你的帮助。

4 个答案:

答案 0 :(得分:21)

Indices trick,yay~

template<class T, class... Args, std::size_t... Is>
T* create(U* p, indices<Is...>){
  return new T(static_cast<Args>(p[Is])...);
}

template<class T, class... Args>
T* create(U* p, std::size_t num_args){
  assert(num_args == sizeof...(Args));
  return create<T, Args...>(p, build_indices<sizeof...(Args)>{});
}

当然,我强烈建议使用smart pointerstd::vector而不是原始指针。

答案 1 :(得分:3)

你需要一个帮手:

#include <tuple>

template <typename T, bool, typename Tuple, unsigned int ...I>
struct helper
{
    static T * go(S * args)
    {
        return helper<T, sizeof...(I) + 1 == std::tuple_size<Tuple>::value,
                      Tuple, I..., sizeof...(I)>::go(args);
    }
};

template <typename T, typename ...Args, unsigned int ...I>
struct helper<T, true, std::tuple<Args...>, I...>
{
    static T * go(S * args)
    {
        return new T(static_cast<Args>(args[I])...);
    }
};

template <typename T, typename ...Args>
T * create(S * args)
{
    return helper<T, sizeof...(Args) == 0, std::tuple<Args...>>::go(args);
}

编辑:经过测试,似乎有效。

答案 2 :(得分:2)

  

假设SomeCastableType可转换为任何类型。显然我无法得到的是INDEX_OF_EXPANSION

C ++ 14 起,您就可以使用std::make_index_sequence帮助程序来完成索引技巧 @Xeo提到的with the support from the standard library。 :

template<class T, class... Args, std::size_t... Is>
T* create(SomeCastableType* p, std::index_sequence<Is...>)
{
    return new T(static_cast<Args>(p[Is])...);
}

template<class T, class... Args>
T* create(SomeCastableType* p, std::size_t num_args)
{
    return create<T, Args...>(p, std::make_index_sequence<sizeof...(Args)>());
}

答案 3 :(得分:1)

如果使用c ++ 17的constexpr,我们可以获得索引查找功能的更具可读性/可理解性的实现(在这里我从未设法绕过其他答案):

template<typename Target, typename ListHead, typename... ListTails>
constexpr size_t getTypeIndexInTemplateList()
{
    if constexpr (std::is_same<Target, ListHead>::value)
        return 0;
    else
        return 1 + getTypeIndexInTemplateList<Target, ListTails...>();
}

可以如下使用:

size_t index = getTypeIndexInTemplateList<X,  Foo,Bar,X,Baz>(); // this will return 2

或者,如果您具有可变模板化类型,并且想要在其中获取索引:

template<typename... Types>
class Container
{
public:
    size_t getIndexOfType<typename T>() {  return getTypeIndexInTemplateList<T, Types...>(); }
};

...

Container<Foo, Bar, X, Baz> container;
size_t container.getIndexOfType<X>(); // will return 2

它的工作方式是从列表中递归消除类型。因此,第一个示例的呼叫顺序基本上是:

getTypeIndexInTemplateList<X,  Foo,  Bar,X,Baz>() // ListHead = Foo, ListTails = Bar,X,Baz
getTypeIndexInTemplateList<X,  Bar,  X,Baz>()     // ListHead = Bar, ListTails = X, Baz
getTypeIndexInTemplateList<X,  X,    Baz>()       // ListHead = X, so now we return. Recursive addition takes care of calculating the correct index

该函数是constexpr,因此将全部在编译时执行,在运行时只是一个常量。

如果您请求列表中不存在的类型,则它将生成编译错误,因为它将尝试使用太少的模板参数来调用该函数。当然,如果该类型存在多次,则只会返回该类型的第一个实例的索引。