【问题标题】:C++ index of type during variadic template expansion可变参数模板扩展期间的 C++ 类型索引
【发布时间】:2013-02-07 11:24:57
【问题描述】:

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

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

非常感谢您的帮助。

【问题讨论】:

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


    【解决方案1】:

    Indices trick,耶~

    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 而不是原始指针。

    【讨论】:

    • 我一直都喜欢这两个,只是为了让事情更简单。
    • 虽然我从不喜欢破坏函数本身,但我认为(从哲学角度)这些构造应该是一种服务,而不是一个细节。
    • @GMan:我不得不承认,我不太明白你所说的“服务”是什么意思。需要详细说明吗?
    • @user:是的,CTP 可变参数非常有问题。索引技巧是我第一次接触它时尝试的一件事,那天晚上我总共提交了 11 个可变参数错误(其中 10 个已在 Microsoft 内部版本中修复)。
    • 您能否包含一个使用 C++14 标准化 std::index_sequence&lt;...&gt; 的版本?或者,也许只使用它,并链接到 here 以实现 C++11 实现?
    【解决方案2】:

    你需要一个帮手:

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

    编辑:经过测试,似乎可以工作。

    【讨论】:

    • 抱歉,此代码不适用于 GCC 4.7.1 和 VS2012 Nov2012 CTP。原因是一个模板参数包必须出现在列表的末尾这可能意味着不支持多个包?
    • @user1191954:是的,已修复:-)
    • @Xeo:我永远记不起那个。随意发布(或编辑)。
    • 完成了,我们为此设立了一个 Lounge Wiki 站点。 :)
    • @Xeo:呵呵,确实!我会在安全的地方写下来。一个小时前我本该下线的,但谁能抗拒可变参数模板?
    【解决方案3】:

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

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

    【讨论】:

    • 我认为,您没有回答 OPs 的问题,但您的答案实际上是我一直在寻找的 ;-)
    【解决方案4】:

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

    C++14开始,您可以索引技巧 @Xeo 提到with the support from the standard library,通过使用std::make_index_sequence 帮助器,如下所示:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 2014-04-12
      • 1970-01-01
      • 2016-04-13
      • 1970-01-01
      • 2014-10-30
      相关资源
      最近更新 更多