[英]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
和遍歷該代替)。
編譯器是正確的。 i
和j
而不是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.