【问题标题】:Data structure of constexpr valuesconstexpr 值的数据结构
【发布时间】:2017-05-16 16:13:56
【问题描述】:

假设您有 std::tuple 和一个函数,该函数根据类型修改其值。

template<typename T, typename Ts...>
void modify(std::tuple<Ts...>& tuple, const T& value)
{
    std::get<T>(tuple) = value;
}

现在假设,您想要记录这些函数调用,即记住元组中值的修改顺序。这是执行此操作的简单机制。

void log(std::size_t index)
{
    static std::vector<std::size_t> logged_indexes;
    logged_indexes.push_back(index)
}

使用以下助手

// counts until T is same as N'th type of Tuple
template<typename Tuple, typename T, std::size_t N>
struct tuple_type_index_impl;

template<typename T, std::size_t N>
struct tuple_type_index_impl<std::tuple<>, T, N>
{
    static constexpr std::size_t value = N;
};

template<typename First, typename... Rest, typename T, std::size_t N>
struct tuple_type_index_impl<std::tuple<First, Rest...>, T, N>
{
    static constexpr std::size_t value = std::is_same<First, T>::value
        ? N
        : tuple_type_index_impl<std::tuple<Rest...>, T, N + 1>::value;
};

// helper variable
template<typename Tuple, Typename T>
constexpr std::size_t tuple_type_index_v = typename tuple_type_index_impl<Tuple, T, 0>::value;

我们可以有这种用法

template<typename T, typename Ts...>
void modify(std::tuple<Ts...>& tuple, const T& value)
{
    std::get<T>(tuple) = value;
    log(tuple_type_index_v<std::tuple<Ts...>, T>);
}

这对于记录目的来说很好。但是假设您必须根据索引再次访问元组。我们可以使用开关运行时索引转换为 constexpr

switch (value)
{
case 0: return std::get<0>(tuple);
case 1: return std::get<1>(tuple);
...
}

可以由编译器生成。但这真的是我们能做的最好的吗?在调用日志时,index 仍然是 constexpr,因此我们可以将其移至模板参数。

template<std::size_t index>
void log()
{
    static std::vector<std::size_t> logged_indexes;
    logged_indexes.push_back(index);
}

但这无济于事,因为我们仍在将它传递给向量。我们可以(?)构建std::index_sequence,但我们最终会用完模板参数。我的一部分认为这不能完成,但其他人反对,因为index 是 constexpr。是否可以开发某种std::vector&lt;constexpr std::size_t&gt;

【问题讨论】:

  • 这个开关或它的替代品是否意味着在日志记录功能中?例如你想记录值和索引吗?
  • @jwimberley 我不介意这些值,只是访问值的索引。该开关由编译器生成,使用类似的方法,例如here
  • 你可以把tuple_type_index_v改成返回std::integral_constant,这样你还是有编译时间值的。
  • @Jarod42 tuple_type_index_v 是编译时间值。
  • 你想从向量内容中检索元组元素,还是从log函数中检索元组元素?

标签: c++ c++14 template-meta-programming


【解决方案1】:

要将您的 switch 转换为“直接”呼叫,您可以执行以下操作:

template <typename ... Ts>
void print(const std::tuple<Ts...>& t, std::size_t index)
{
    if (sizeof...(Ts) < index) {
        throw std::runtime_error("invalid index");
    }
    using func_t = void (*)(const std::tuple<Ts...>&);
    func_t fs[] = { (+[](const std::tuple<Ts...>& t){
             std::cout << std::get<Ts>(t) << std::endl;
        })...
        // gcc dislikes that lambda,
        // but you can create template function instead
    };

    fs[index](t);
}

Demo

【讨论】:

  • 我实际测试过,性能和switch一样,编译器可以优化一下。
猜你喜欢
  • 1970-01-01
  • 2017-09-27
  • 1970-01-01
  • 1970-01-01
  • 2017-05-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 2015-08-13
相关资源
最近更新 更多