为什么用成员数组调用constexpr函数而不是常量表达式?

时间:2018-06-18 20:57:26

标签: c++ templates constexpr

我有以下辅助函数:

template<typename T, std::size_t N>
constexpr std::size_t Length(const T(&)[N]) {
    return N;
}

返回静态数组的长度。在过去,这总是有效,但是当我这样做时:

struct Foo
{
    unsigned int temp1[3];
    void Bar()
    {
        constexpr std::size_t t = Length(temp1); // Error here
    }
};

使用MSVS 2017时出错:

error C2131: expression did not evaluate to a constant

note: failure was caused by a read of a variable outside its lifetime

note: see usage of 'this'

我希望有人能说清楚我做错了什么。

3 个答案:

答案 0 :(得分:21)

MSVC是正确的。 Length(temp1)不是一个常量表达式。来自[expr.const]p2

  

表达式 e 是核心常量表达式,除非根据抽象机器的规则评估 e 将评估以下表达式之一:

     
      
  • this,但constexpr函数或constexpr构造函数除外,该构造函数作为 e 的一部分进行评估;
  •   

temp1隐式评估this(因为您指的是this->temp1),因此您不会有常量表达式。 gcc和clang接受它,因为它们支持VLA作为扩展(尝试使用-Werror=vla-pedantic-errors进行编译。)

为什么不允许这样做?好吧,您可以访问底层元素并可能修改它们。如果你正在处理一个constexpr数组或一个被评估为常量表达式的数组,这是完全正常的,但如果你不是,那么你就不可能有一个常量表达式,因为你将操纵值在运行时设置。

答案 1 :(得分:4)

Length(decltype(temp1){})

seems to work

不幸的是,我无法评论,但Mehrdad的解决方案是错误的。原因是:它是 技术未定义的行为 未定义的行为。在constexpr评估期间,编译器必须捕获未定义的行为。因此,the code is ill-formed

答案 2 :(得分:1)

您的问题已经得到解答,但就如何解决问题而言#34;它,一种快速而肮脏的方式是取代

Length(temp1)

Length(*(true ? NULL : &temp1))

我认为技术上未定义的行为,但实际上可以在MSVC上正常工作。

如果您需要一个尽管有UB的解决方案,您可以更改Length以使用指针:

template<typename T, std::size_t N>
constexpr std::size_t Length(const T(*)[N]) {
    return N;
}

然后您可以使用Length(true ? NULL : &temp1)