簡體   English   中英

C++11:數組的編譯時間計算

[英]C++11: Compile Time Calculation of Array

假設我有一些 constexpr function f:

constexpr int f(int x) { ... }

而且我在編譯時知道一些 const int N :

任何一個

#define N ...;

或者

const int N = ...;

根據您的回答需要。

我想要一個 int 數組 X:

int X[N] = { f(0), f(1), f(2), ..., f(N-1) }

這樣 function 在編譯時進行評估,X 中的條目由編譯器計算,結果放置在我的應用程序映像的 static 區域中,就像我在我的初始值列表中使用 Z157DB7DF530023575515ZD36E 一樣。

有什么辦法可以寫這個嗎? (例如使用模板或宏等)

最好的:(感謝 Flexo)

#include <iostream>
#include <array>
using namespace std;

constexpr int N = 10;
constexpr int f(int x) { return x*2; }

typedef array<int, N> A;

template<int... i> constexpr A fs() { return A{{ f(i)... }}; }

template<int...> struct S;

template<int... i> struct S<0,i...>
{ static constexpr A gs() { return fs<0,i...>(); } };

template<int i, int... j> struct S<i,j...>
{ static constexpr A gs() { return S<i-1,i,j...>::gs(); } };

constexpr auto X = S<N-1>::gs();

int main()
{
        cout << X[3] << endl;
}

這個問題有一個純C ++ 11(沒有提升,也沒有宏)解決方案。 使用與此答案相同的技巧,我們可以構建一個數字序列並解壓縮它們以調用f來構造一個std::array

#include <array>
#include <algorithm>
#include <iterator>
#include <iostream>

template<int ...>
struct seq { };

template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };

template<int ...S>
struct gens<0, S...> {
  typedef seq<S...> type;
};

constexpr int f(int n) {
  return n;
}

template <int N>
class array_thinger {
  typedef typename gens<N>::type list;

  template <int ...S>
  static constexpr std::array<int,N> make_arr(seq<S...>) {
    return std::array<int,N>{{f(S)...}};
  }
public:
  static constexpr std::array<int,N> arr = make_arr(list()); 
};

template <int N>
constexpr std::array<int,N> array_thinger<N>::arr;

int main() {
  std::copy(begin(array_thinger<10>::arr), end(array_thinger<10>::arr), 
            std::ostream_iterator<int>(std::cout, "\n"));
}

(用g ++ 4.7測試)

你可以完全跳過std::array ,但是我認為在這個實例中使用std::array更簡潔。

您也可以遞歸執行此操作:

#include <array>
#include <functional>
#include <algorithm>
#include <iterator>
#include <iostream>

constexpr int f(int n) {
  return n;
}

template <int N, int ...Vals>
constexpr
typename std::enable_if<N==sizeof...(Vals),std::array<int, N>>::type
make() {
  return std::array<int,N>{{Vals...}};
}

template <int N, int ...Vals>
constexpr
typename std::enable_if<N!=sizeof...(Vals), std::array<int,N>>::type 
make() {
  return make<N, Vals..., f(sizeof...(Vals))>();  
}

int main() {
  const auto arr = make<10>();
  std::copy(begin(arr), end(arr), std::ostream_iterator<int>(std::cout, "\n"));
}

這可以說更簡單。

Boost.Preprocessor可以幫到你。 但是,限制是你必須使用整數文字,如10而不是N (甚至是編譯時常量):

#include <iostream>

#include <boost/preprocessor/repetition/enum.hpp>

#define VALUE(z, n, text) f(n)

//ideone doesn't support Boost for C++11, so it is C++03 example, 
//so can't use constexpr in the function below
int f(int x) { return x * 10; }

int main() {
  int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) };  //N = 10
  std::size_t const n = sizeof(a)/sizeof(int);
  std::cout << "count = " << n << "\n";
  for(std::size_t i = 0 ; i != n ; ++i ) 
    std::cout << a[i] << "\n";
  return 0;
}

輸出( ideone ):

count = 10
0
10
20
30
40
50
60
70
80
90

以下行中的宏:

int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) }; 

擴展到這個:

int const a[] = {f(0), f(1), ... f(9)}; 

更詳細的解釋如下:

如果您希望陣列存在於靜態內存中,您可以嘗試這樣做:

template<class T> struct id { typedef T type; };
template<int...> struct int_pack {};
template<int N, int...Tail> struct make_int_range
    : make_int_range<N-1,N-1,Tail...> {};
template<int...Tail> struct make_int_range<0,Tail...>
    : id<int_pack<Tail...>> {};

#include <array>

constexpr int f(int n) { return n*(n+1)/2; }

template<class Indices = typename make_int_range<10>::type>
struct my_lookup_table;
template<int...Indices>
struct my_lookup_table<int_pack<Indices...>>
{
    static const int size = sizeof...(Indices);
    typedef std::array<int,size> array_type;
    static const array_type& get()
    {
        static const array_type arr = {{f(Indices)...}};
        return arr;
    }
};

#include <iostream>

int main()
{
    auto& lut = my_lookup_table<>::get();
    for (int i : lut)
        std::cout << i << std::endl;
}

如果您想要處理數組的本地副本,只需刪除&符號。

我稍微擴展了Flexo和Andrew Tomazos的答案,以便用戶可以指定計算范圍和要評估的函數。

#include <array>
#include <iostream>
#include <iomanip>

template<typename ComputePolicy, int min, int max, int ... expandedIndices> 
struct ComputeEngine
{
  static const int lengthOfArray = max - min + sizeof... (expandedIndices) + 1;
  typedef std::array<typename ComputePolicy::ValueType, lengthOfArray> FactorArray;

  static constexpr FactorArray compute( )
  {
    return ComputeEngine<ComputePolicy, min, max - 1, max, expandedIndices...>::compute( );
  }
};

template<typename ComputePolicy, int min, int ... expandedIndices> 
struct ComputeEngine<ComputePolicy, min, min, expandedIndices...>
{
  static const int lengthOfArray = sizeof... (expandedIndices) + 1;
  typedef std::array<typename ComputePolicy::ValueType, lengthOfArray> FactorArray;

  static constexpr FactorArray compute( )
  {
    return FactorArray { { ComputePolicy::compute( min ), ComputePolicy::compute( expandedIndices )... } };
  }
};

/// compute 1/j
struct ComputePolicy1
{
  typedef double ValueType;

  static constexpr ValueType compute( int i )
  {
    return i > 0 ? 1.0 / i : 0.0;
  }
};

/// compute j^2
struct ComputePolicy2
{
  typedef int ValueType;

  static constexpr ValueType compute( int i )
  {
    return i * i;
  }
};

constexpr auto factors1 = ComputeEngine<ComputePolicy1, 4, 7>::compute( );
constexpr auto factors2 = ComputeEngine<ComputePolicy2, 3, 9>::compute( );

int main( void )
{
  using namespace std;

  cout << "Values of factors1" << endl;
  for ( int i = 0; i < factors1.size( ); ++i )
  {
    cout << setw( 4 ) << i << setw( 15 ) << factors1[i] << endl;
  }
  cout << "------------------------------------------" << endl;

  cout << "Values of factors2" << endl;
  for ( int i = 0; i < factors2.size( ); ++i )
  {
    cout << setw( 4 ) << i << setw( 15 ) << factors2[i] << endl;
  }

  return 0;
}

這里有很多很棒的答案。 問題和標簽指定c++11 ,但是幾年過去了,一些(像我一樣)絆倒這個問題可能會對使用c++14開放。 如果是這樣,可以使用std::integer_sequence非常干凈簡潔地完成這項工作; 此外,它可用於實例化更長的數組,因為當前的“我有最好的”受到遞歸深度的限制。

constexpr std::size_t f(std::size_t x) { return x*x; } // A constexpr function
constexpr std::size_t N = 5; // Length of array

using TSequence = std::make_index_sequence<N>;

static_assert(std::is_same<TSequence, std::integer_sequence<std::size_t, 0, 1, 2, 3, 4>>::value,
"Make index sequence uses std::size_t and produces a parameter pack from [0,N)");

using TArray = std::array<std::size_t,N>;

// When you call this function with a specific std::integer_sequence,
// the parameter pack i... is used to deduce the the template parameter
// pack.  Once this is known, this parameter pack is expanded in
// the body of the function, calling f(i) for each i in [0,N).

template<std::size_t...i>
constexpr TArray
get_array(std::integer_sequence<std::size_t,i...>)
{
  return TArray{{ f(i)... }}; 
}

int main()
{

  constexpr auto s = TSequence();
  constexpr auto a = get_array(s);

  for (const auto &i : a) std::cout << i << " ";  // 0 1 4 9 16

  return EXIT_SUCCESS;

}

這是一個更簡潔的答案,您可以在其中明確聲明原始序列中的元素。

#include <array>

constexpr int f(int i) { return 2 * i; }

template <int... Ts>
struct sequence
{
    using result = sequence<f(Ts)...>;
    static std::array<int, sizeof...(Ts)> apply() { return {{Ts...}}; }
};

using v1 = sequence<1, 2, 3, 4>;
using v2 = typename v1::result;

int main()
{
    auto x = v2::apply();
    return 0;
}

這個怎么樣?

#include <array>
#include <iostream>

constexpr int f(int i) { return 2 * i; }

template <int N, int... Ts>
struct t { using type = typename t<N - 1, Ts..., 101 - N>::type; };

template <int... Ts>
struct t<0u, Ts...>
{
    using type = t<0u, Ts...>;
    static std::array<int, sizeof...(Ts)> apply() { return {{f(Ts)...}}; }
};

int main()
{
    using v = typename t<100>::type;
    auto x = v::apply();
}

我認為這不是最好的方法,但可以嘗試這樣:

#include <array>
#include <iostream>
#include <numbers>

constexpr auto pi{std::numbers::pi_v<long double>};

template <typename T>
struct fun
{
    T v;
    explicit constexpr fun(T a) : v{a * a} {}
};

template <size_t N, typename T, typename F>
struct pcl_arr
{
    std::array<T, N> d;

    explicit constexpr pcl_arr()
    : d{}
    {
        for (size_t i{}; i < N; d[i] = !i ? 0. : F(pi + i).v, ++i);
    }
};

int main()
{
    using yummy = pcl_arr<10, long double, fun<long double>>;

    constexpr yummy pies;
    
    std::array cloned_pies{pies.d};

    //  long double comparison is unsafe
    //  it's just for the sake of example
    static_assert(pies.d[0] == 0.);

    for (const auto & pie : pies.d)         { std::cout << pie << ' '; } std::cout << '\n';
    for (const auto & pie : cloned_pies)    { std::cout << pie << ' '; } std::cout << '\n';

    return 0;
}

godbolt.org x86-x64 gcc 11.2 -Wall -O3 -std=c++20 output:

0 17.1528 26.436 37.7192 51.0023 66.2855 83.5687 102.852 124.135 147.418

0 17.1528 26.436 37.7192 51.0023 66.2855 83.5687 102.852 124.135 147.418

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM