变量模板扩展期间类型的C++索引

C++ index of type during variadic template expansion

本文关键字:类型 C++ 索引 扩展 变量      更新时间:2023-10-16

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

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

非常感谢你的帮助。

指数技巧,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)>{});
}

当然,我强烈建议使用智能指针和std::vector,而不是原始指针。

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

由于C++14,您可以使用std::make_index_sequence助手在标准库的支持下执行@Xeo提到的索引技巧,如下所示:

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

使用c++17的constexpr-if,我们可以获得一个更可读/更易懂的索引查找函数实现(我从未想过这里的其他答案):

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,所以这一切都将在编译时执行,在运行时它只是一个常量。

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

您需要一个助手:

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

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