【问题标题】:Cannot make std::array a constexpr in C++无法在 C++ 中使 std::array 成为 constexpr
【发布时间】:2018-11-24 23:56:29
【问题描述】:

在下面的代码中,VS2017 出现“错误 C3615: constexpr function 'to_array' cannot result in a constant expression' 编译器错误:

#include <stdio.h>
#include <array>

template <typename T>
static constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
{
    std::array<std::uint8_t, sizeof(T)> result {};

    for (std::size_t i{ sizeof(T) }; i != 0 ; --i)
    {
        result[i - 1] = static_cast<uint8_t>(value >> ((sizeof(T) - i) * 8));
    }

    return result;
}

int main()
{
    constexpr uint64_t sample = UINT64_C(0xab28ecb46814fe75);

    //error C3615: constexpr function 'to_array' cannot result in a constant expression
    constexpr auto a = to_array(sample);

    return 0;
}

如果理论上是std::array can be constexpr,为什么会出现这里的错误?

编辑1:

它在没有循环的情况下编译:

template <typename T>
static constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
{
    std::array<std::uint8_t, sizeof(T)> result {};

    //this is OK
    return result;
}

EDIT2:

循环的完整错误信息是:

error C3615: constexpr function 'to_array' cannot result in a constant expression
note: failure was caused by an uninitialized variable declaration
note: see usage of 'result'

【问题讨论】:

    标签: c++


    【解决方案1】:

    如果理论上是std::array can be constexpr,为什么会出现这里的错误?

    您提供的链接正在处理构建std::array 作为constexpr 的一部分,而不是像您那样对它做任何其他事情。

    它在没有循环的情况下编译

    您的错误与循环本身没有任何关系,因为它直接与在 constexpr 函数体中使用 std::array 的 operator[] 相关。

    operator[] 的 std::array is constexpr since C++17,但 it seem MSVC 19 hasn't implemented it 即使它在规范中。因此,在您的情况下,启用 C++17 构建(编译器标志 /std:c++latest)不会直接解决问题:-(

    好消息是MSVC 20 is conforming just fine :-)

    【讨论】:

    • 我认为即使使用/std:c++17,OP 的代码也会让 MSVC 不满意。
    • 我在 MSVC 2017 中使用 ISO C++ 最新草案标准 (/std:c++latest) 进行编译
    • @AlexeyStarinsky,那么请引用编译器发出的完整错误消息。
    • 用 VS2017 15.8.2 和 /std:c++17 编译就好了
    【解决方案2】:

    完整的错误信息是:

    error C2131: expression did not evaluate to a constant
    note: failure was caused by call of undefined function or one not declared 'constexpr'
    note: see usage of 'std::array<uint8_t,8>::operator []'
    note: while evaluating 'to_array(12333367839138578037)'
    fatal error C1903: unable to recover from previous error(s); stopping compilation
    

    如果我们现在看一下 operator[] 的声明,我们会发现在那个版本的 Microsoft STD 实现中它还不是 constexpr:

    reference operator[](_In_range_(0, _Size - 1) size_type _Pos)
    

    解决方法是使用 C 数组,然后将其转换为 std::array:

    template<std::size_t... is, std::size_t n = sizeof...(is)>
    constexpr std::array<std::uint8_t, n>
        to_array(std::uint8_t (&arr)[n], std::index_sequence<is...>)
    {
        return {arr[is]...};
    }
    
    template <typename T>
    constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
    {
        std::uint8_t result[sizeof(T)];
        ...
        return to_array(result, std::make_index_sequence<sizeof(T)>{});
    }
    

    或

    template<typename T, std::size_t... is>
    constexpr std::array<std::uint8_t, sizeof(T)> 
        to_array(T value, std::index_sequence<is...>)
    {
        return {static_cast<std::uint8_t>(value >> ((sizeof(T) - is - 1) * 8))...};
    }
    
    template <typename T>
    constexpr std::array<std::uint8_t, sizeof(T)> to_array(T value)
    {
        return to_array(value, std::make_index_sequence<sizeof(T)>{});
    }
    

    但无论如何,您都不能在constexpr 上下文中的结果上使用operator[]。

    【讨论】:

      【解决方案3】:

      我今天遇到了这个问题。我决定尝试包装 std::array 以查看是否可以添加 constexpr 支持。我刚刚开始,这恰好在我重载 [] 运算符之前起作用。

      template <typename T, unsigned S> struct _array : public std::array<T,S> {};
      

      现在可以使用 _array 代替 std::array。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-25
        • 1970-01-01
        • 2019-01-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-18
        相关资源
        最近更新 更多