2017-09-01 119 views
0
#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與以下編譯錯誤:編譯時間類型代constexpr功能

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

如果我們假設函數(和相應的循環)在編譯時被評估(這對於從C++ 14開始的循環是可行的),爲什麼這個代碼不能儘管i未被聲明爲const,但它實際上可以是constexpr,因爲它的所有值在編譯時也是已知的。

您能否澄清一下這段代碼是否因其思想而無效?或者有一個編譯器限制?或者以下都不是?

回答

2

Could you please clarify whether this code is invalid by its very idea?

這是 - 你要使用一個可變的和有狀態的迭代變量作爲常量表達式恆定表達式的整個概念圍繞着不變性展開。編譯期間是否執行循環無關緊要。

你真正應該做的是在這裏產生代碼以下片段:

{ 
    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>{}); 
} 

live example on wandbox

注意過for_each_arg更高層次的抽象,可以很容易地提供(如迭代數的編譯時間範圍,或轉換constexpr陣列到一個integral_constant的序列,並代之以迭代)

0

每個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)。

1

編譯器是正確的。 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; 
    }); 
}