std :: initializer_list :: size()与std :: array :: size()的constexpr-ness

时间:2019-11-26 19:39:05

标签: c++ c++17 constexpr initializer-list

size()std::initializer_list的{​​{1}}成员函数具有相同的签名:

std::array

两者均为constexpr size_type size() const noexcept; 。但是,constexpr可以在std::array::size()上下文中使用,但是constexpr不能:

std::initializer_list::size()

(*)错误是:

std::initializer_list<int> il{1, 2, 3, 4};
constexpr std::size_t il_size = il.size();       // (1) - fails with GCC and Clang (*)

std::array<int, 4> arr{1, 2, 3, 4};
constexpr std::size_t arr_size = arr.size();     // (2) - OK

As far as I understand,因为(1)失败而(2)成功,这一事实是完全合理的,因为类模板的in 'constexpr' expansion of 'il.std::initializer_list<int>::size()' error: the value of 'il' is not usable in a constant expression 成员函数可能无法满足constexpr的要求。

我有两个相关的问题:

  1. 为什么constexpr不能以std::initializer_list的编译方式实现?标准中是否有阻止这种实现的措施?
  2. 鉴于(1)失败,将(1)标记为std::initializer_list::size()的目的是什么?唯一的用例似乎就是这个:

    constexpr

2 个答案:

答案 0 :(得分:4)

  

为什么std::initializer_list的实现方式不像(1)那样编译?标准中是否有某些东西阻止这种实现?

是的,这是不可能的。 initializer_list可以具有任何大小,您不能在恒定的评估时间内获得任意运行时initializer_list的大小。这与std::array完全不同,在std::array<T, N>中,给定的N的大小为struct X { int i; }; X x{42}; constexpr X cx{17}; constexpr int i = x.i; // error constexpr int ci = cx.i; // ok 。一个人的大小是可变的,另一个人的大小是固定的。

这与其他任何变量都没有真正的区别:

std::initializer_list::size()
  

鉴于(1)失败,将constexpr标记为constexpr的目的是什么?唯一的用例似乎就是这个

这不是唯一的用例,远非如此。 constexpr个成员函数不仅允许您在initializer_list个对象上调用它们。通常,它们使您可以在持续评估时间内在任何地方调用它们。

也就是说,在进行任何形式的持续评估时,如果创建constexpr size_t four() { std::initializer_list<int> lst = {1, 2, 3, 4}; return lst.size(); } static_assert(four() == 4); ,则可以使用其大小。一个愚蠢的最小示例可能是:

lst

请注意,constexpr本身不是four()对象,它只是在不断求值期间评估对size()的调用的过程中创建的一些短暂的东西。但是我们仍然需要constexpr成为constexpr-调用任何非std::initializer_list函数都是不行的。

从这里您可以将其向外扩展到您可能想要运行的任意代码,在持续评估期间的某个时刻,它想要确定\((?!http)的大小。

答案 1 :(得分:2)

不是std::initializer_list::size不能在常量表达式中使用。例如,以下可能(请参见脚注)should compile

#include <initializer_list>
int main() {
    constexpr int x = (std::initializer_list<int>{1, 2, 3, 4}).size();
    static_assert(x == 4);
}

但是,您的调用方式大概是size方法必须对std::initializer_list对象的一个​​成员执行从左到右的转换,这违反了约束。关于常量表达式(C ++ 17 [expr.const] /(2.7))。

在我的示例中,相应的从左值到右值的转换发生在“文字类型的非易失性glvalue上,该值引用寿命在其内开始的非易失性对象 e;的求值”,这使其可以使用(C ++ 17 [expr.const] /(2.7.4))。

std::array示例中,由于返回了模板参数,因此可能没有左值到右值的转换。

脚注:请参见LWG 2833