簡體   English   中英

constexpr函數中的編譯時類型生成

[英]Compile-time types generation in constexpr functions

#include <array>
#include <tuple>

typedef std::tuple<const int> TupleType;

constexpr std::array<const int, 2> a = {1, 2};

constexpr void foo()
{
    for (std::size_t i = 0; i < a.size(); ++i)
    {
        const int j = i;
        typedef std::tuple_element<j, TupleType> T;
    }
}

gcc-7.2無法使用--std = c ++ 17編譯代碼 ,但出現以下編譯錯誤:

error: the value of 'j' is not usable in a constant expression
note: in template argument for type 'long unsigned int'

如果我們假設函數(和相應的循環)是在編譯時評估的(這對於從c ++ 14開始的循環是可行的),那么為什么即使未聲明為,該代碼也無法編譯得那么遠。 const ,實際上它可以是constexpr,因為它的所有值在編譯時也是已知的。

您能否從本質上澄清此代碼是否無效? 還是有編譯器限制? 還是以下都不是?

您能否從本質上澄清此代碼是否無效?

是-您正在嘗試使用可變且有狀態的迭代變量作為常量表達式 常量表達式的整個概念圍繞不變性。 循環是否在編譯期間執行並不重要。

您實際上應該在此處為以下代碼段生成代碼

{
    typedef std::tuple_element<j, TupleType> T;
    // ...
}

其中j是一個常量表達式的占位符。 這是一種可行的方法:

template <typename F, typename... Ts>
constexpr void for_each_arg(F&& f, Ts&&... xs)
{
    (f(std::forward<Ts>(xs)), ...);
}

constexpr void foo()
{
    for_each_arg([](auto c)
    {
        typedef std::tuple_element<c, TupleType> T;
    },
    std::integral_constant<int, 1>{}, 
    std::integral_constant<int, 2>{});
}

魔盒上的現場示例

請注意,較高級別的抽象超過for_each_arg可以很容易地提供(例如迭代數的編譯時間范圍,或一個轉換constexpr陣列的序列integral_constant和遍歷該代替)。

編譯器是正確的。 ij而不是constexpr。 尋找自己:

//    v--- not constexpr
for (std::size_t i = 0; i < a.size(); ++i)
    {
        // Not constexpr either
        const int j = i;
        typedef std::tuple_element<j, TupleType> T;
    }

如果您嘗試標記j constexpr,您會發現由於i不在,所以事實並非如此。

如果你試圖聲明i constexpr,你會看到,constexpr變量都受到相同的規則為任何constexpr變量:你不能變異他們。

那么如何遍歷數字以生成類型呢?

您可以將包擴展與索引序列一起使用:

template<typename T, T... S, typename F>
void for_sequence(std::integer_sequence<S...>, F f)
{
    using unpack = int[];
    (void) unpack{(f(std::integral_constant<T, S>{}), void(), 0)..., 0};
}

constexpr void foo()
{
    for_sequence(std::make_index_sequence<a.size()>{}, [](auto i)
    {
        typedef std::tuple_element<i, TupleType> T;
    });
}

每個constexpr函數都必須能夠在運行時進行評估。

constexpr並不表示“必須在編譯時運行”,而是表示“可以在編譯時運行”。

沒有根本原因,為什么沒有constexpr for循環使索引在每次迭代時成為constexpr值。 但是C ++沒有。

它的確具有constexpr if它在本質上與您想要的東西相似。

在此之前,您必須自己編寫。

template<std::size_t...Is>
constexpr auto index_over( std::index_sequence<Is...> ){
  return [](auto&& f){
    return decltype(f)(f)( std::integral_constant<std::size_t, Is >{}... );
  };
}
template<std::size_t N>
constexpr auto index_upto( std::integral_constant<std::size_t,N> ={} ){
  return index_over( std::make_index_sequence<N>{} );
}
template<class F>
constexpr auto foreacher(F&&f){
  return [f](auto&&...args){
    ( (void)(f(decltype(args)(args)), ... );
  };
}

constexpr void foo()
{
  index_upto<a.size()>()(foreacher([](auto I){
    typedef std::tuple_element<I, TupleType> T;
  });
}

是C ++ 17中未編譯的示例(主要是17,因為它具有constexpr lambdas)。

暫無
暫無

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

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