我可以从循环变量创建一个integral_constant吗?

时间:2021-06-14 18:15:39

标签: c++

我尝试从块矩阵中提取所有单个块。但是,我使用的库中的 BlockMatrixClass 只允许通过以下索引访问:
Dune::index_constant<0>(), Dune::index_constant<1>(),...
它们解析为 std::integral_constant().

如何将以下代码更改为有效?无需更改库。

for(std::size_t i=0; i<3; i++){
  for(std::size_t j=0; j<3; j++){
    const auto& m = jacobian[Dune::index_constant<i>()][Dune::index_constant<j>()];
  }
}

目前我收到错误

error: the value of ‘j’ is not usable in a constant expression
  401 | t auto& m = jacobian[Dune::index_constant<i>()][Dune::index_constant<j>()];

3 个答案:

答案 0 :(得分:3)

迭代编译时间序列并不是一个完全解决的问题,因为它们都依赖于各种解决方法,并且有很多方法可以做到。

我的首选方法是使用索引序列,并使用折叠表达式展开它:

简单定义:

template<typename T, T... S, typename F>
constexpr void for_sequence(std::integer_sequence<T, S...>, F&& f) {
    (static_cast<void>(f(std::integral_constant<T, S>{})), ...);
}

然后,像这样使用它:

for_sequence(std::make_index_sequence<3>{}, [&](auto i) {
    for_sequence(std::make_index_sequence<3>{}, [&](auto j) {
        const auto& m = jacobian[Dune::index_constant<i>()][Dune::index_constant<j>()];
    });
});

这里的变量 ij 是整型常量,可以用在模板参数中。您发送给函数的实际 std::integer_sequence 决定了实例化最内层 lambda 的值。

如果jacobian可以直接使用那些积分常数而不是它们自己的Dune::index_constant,你可以简单地写成:

for_sequence(std::make_index_sequence<3>{}, [&](auto i) {
    for_sequence(std::make_index_sequence<3>{}, [&](auto j) {
        const auto& m = jacobian[i][j];
    });
});

答案 1 :(得分:1)

#include <cstdint>
#include <cstddef>
#include <utility>

template <size_t index, size_t end>
void for_loop () {
    if constexpr (index == end) {
        return;
    }

    constexpr size_t i = index / 3;
    constexpr size_t j = index % 3;
    const auto& m = jacobian[Dune::index_constant<i>()][Dune::index_constant<j>()];

    impl<index + 1, end>();
}

template <size_t end>
void for_loop_to() {
    for_loop<0, end>();
}

int main ()
{
    for_loop_to<9>();
}

这样的东西对 constexpr for 循环类型的东西有用吗?这适用于 C++17(因为它需要 constexpr if),但更详细的版本将在早期的 C++ 版本中实现。此外,我没有将 jacobian 连接到内部函数,但您可以使用结构包装器来实现。

答案 2 :(得分:1)

您可以展开循环:

const auto& m = jacobian[Dune::index_constant<0>()][Dune::index_constant<0>()];
const auto& m = jacobian[Dune::index_constant<0>()][Dune::index_constant<1>()];
const auto& m = jacobian[Dune::index_constant<0>()][Dune::index_constant<2>()];

const auto& m = jacobian[Dune::index_constant<1>()][Dune::index_constant<0>()];
const auto& m = jacobian[Dune::index_constant<1>()][Dune::index_constant<1>()];
const auto& m = jacobian[Dune::index_constant<1>()][Dune::index_constant<2>()];

const auto& m = jacobian[Dune::index_constant<2>()][Dune::index_constant<0>()];
const auto& m = jacobian[Dune::index_constant<2>()][Dune::index_constant<1>()];
const auto& m = jacobian[Dune::index_constant<2>()][Dune::index_constant<2>()];

如果那不是一个选项,您可以应用某种形式的编译时间:

#include <utility>
#include <iostream>

template <template<size_t> class F,size_t ... I>
void for_impl(std::index_sequence<I...>){
    (F<I>{}(),...);
}

template <template<size_t> class F,std::size_t N,typename Indices = std::make_index_sequence<N>>
void for_(){
    for_impl<F>(Indices{});
}

template <size_t i>
struct foo{
    void operator()() {
        std::cout << i << "\n";
    }
};


int main() {
    for_<foo,5>();
}

这里只概述了一个循环。我不确定您是否需要在编译时展开两个循环。在示例中,foo 是将循环体包裹在其中的模板。