【问题标题】:Get type for index in tuple during runtime在运行时获取元组中索引的类型
【发布时间】:2013-12-14 20:45:27
【问题描述】:

我知道,有几个主题,询问非常接近的事情,但我没有得到它在我的情况下工作。

我想在运行时构建一个具有索引访问的模板化工厂。因此,我有几种具有相同基本类型的类型。工厂获取每个模板参数能够生成的类型。对工厂的调用只是给出了一个索引。这是一个小例子:

#include <iostream>
#include <memory>
#include <tuple>

struct Base {
};

struct A : Base {
  A(int) { std::cout << "A" << std::endl; }
};
struct B : Base {
  B(int) { std::cout << "B" << std::endl; }
};
struct C : Base {
  C(int) { std::cout << "C" << std::endl; }
};

template <typename ... Types>
struct Factory {
  typedef std::tuple<Types...> TypesTuple;

  std::shared_ptr<Base> operator ()(int index) {
    return produce(index);
  }

  std::shared_ptr<Base> produce(int index) {
    switch (index) {
    case 0: return std::make_shared<typename std::tuple_element<0, TypesTuple>::type>(42);
    case 1: return std::make_shared<typename std::tuple_element<1, TypesTuple>::type>(42);
    }
    throw; 
  }
};

//==============================================================================
int main() {    
  Factory<A, C> factory_ac;
  auto a1 = factory_ac(0);
  auto c1 = factory_ac(1);

  Factory<A, B, C> factory_bc;
  auto a2 = factory_bc(0);
  auto b2 = factory_bc(1);
  auto c2 = factory_bc(2);
}

我试图用

重载生产方法
template <typename = typename std::enable_if<std::tuple_size<TypesTuple>::value==2>::type>

计算大小并提供相应的switch 语句,但这不会编译,不允许重载。

我尝试使用https://stackoverflow.com/a/7383493/2524462,但我无法让它工作,因为参数包没有使用 lambda 扩展并将其包装在模板函数中我遇到了constexpr 数组的问题,因为我没有'没有平凡的类型。

我想到了 Boost MPL for_each,但我在编译时遇到了问题,因为我的类型不是可简单构造的。

那么如何更改工厂以使 main 编译和工作?

【问题讨论】:

  • 我理解你的目标是否正确,你想要一个依赖于整数参数的工厂只是从工厂的模板参数列表中创建一个相应类型的对象? (嗯,实际上是一个std::shared_ptr&lt;Base&gt; 指向相应类型的对象)

标签: c++ c++11 tuples template-meta-programming


【解决方案1】:

这似乎可以很直接地完成:

template <typename T>
std::shared_ptr<Base> make() {
    return std::make_shared<T>();
}
template <typename... T>
class Factory {
public:
    std::shared_ptr<Base> operator()(int index) {
        static constexpr std::shared_ptr<Base> (*factories[])() = {
            &make<T>...
        };
        if (index < 0 && sizeof...(T) <= index) {
            throw std::range_error("type index out of range");
        }
        return (factories[index])();
    }
};

我目前无法编译代码,但类似这样的东西应该可以工作:这个想法是创建一个工厂函数数组,然后调用这个数组。

【讨论】:

  • 我比我更喜欢你的解决方案。非常好。
  • @DyP:嗯,在数组中我不认为我可以使用&amp;std::make_shared&lt;T&gt;...,因为元素都有不同的类型,不兼容。但是,我想我可以在make&lt;T&gt;() 函数中使用std::make_shared&lt;T&gt;()(并相应地更改了答案)。感谢您修复错误!
  • 我认为一个小的改进可能是使数组constexpr,或至少const
  • @DyP:嗯。是的,我想这是真的。现在问题变成了我需要将constexpr 放在哪里:) 我认为它在* 之后。
  • @DietmarKühl 它将代替 static
【解决方案2】:

如果我正确理解了您的要求,我认为这就是您想要的:

template<int... Is>
struct indices { typedef indices type; };

template<int N, int... Is>
struct make_indices : make_indices<N - 1, N - 1, Is...> { };

template<int... Is>
struct make_indices<0, Is...> : indices<Is...> { };

template<typename... Types>
struct Factory
{
    typedef std::tuple<Types...> TypesTuple;

    std::shared_ptr<Base> operator()(int const index)
    {
        return produce(index);
    }

    std::shared_ptr<Base> produce(int const index)
    {
        return produce_impl(make_indices<sizeof...(Types)>(), index);
    }

    template<int I, int... Is>
    std::shared_ptr<Base> produce_impl(indices<I, Is...>, int const index)
    {
        if (I == index) {
            return std::make_shared<typename std::tuple_element<I, TypesTuple>::type>(42);
        }

        return produce_impl(indices<Is...>(), index);
    }

    std::shared_ptr<Base> produce_impl(indices<>, int const index)
    {
        throw "Uh-oh!";
    }
};

查看输出here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 2016-06-25
    • 1970-01-01
    相关资源
    最近更新 更多