2017-09-01 68 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; 
    }); 
}