在模板参数中使用 constexpr 向量 (C++20)

Using constexpr vectors in template parameters (C++20)

最近,我一直在玩弄 C++20 的新 constexpr std::vectors(我使用的是 GCC v12),运行 遇到了一个小问题(这实际上是一个 的扩展名,但我认为制作一个新的会更好)。我一直在尝试使用 constexpr std::vectors 作为 class 的成员,但这似乎不起作用,因为你不能用 constexpr 注释它们,因此 constexpr函数认为它们不能在编译时求值,所以现在我尝试使用模板参数,如下所示:

#include <array>

template<int N, std::array<int, N> arr = {1}>
class Test
{
public:
    constexpr int doSomething()
    {
        constexpr const int value = arr[0];
        return value * 100;
    }
};

int main()
{
    Test<10> myTestClass;
    // return the value to prevent it from being optimized away 
    return myTestClass.doSomething();
}

这会产生预期的汇编输出(仅从 main 返回 100):

main:
        mov     eax, 100
        ret

但是,这样的事情对 std::vector 人不起作用, 即使他们现在可以 constexpr

#include <vector>

template<std::vector<int> vec = {1}>
class Test
{
public:
    constexpr int doSomething()
    {
        constexpr const int value = vec[0];
        return value * 100;
    }
};

int main()
{
    Test<> myTestClass;
    return myTestClass.doSomething();
}

这会引发此错误:

<source>:3:35: error: 'std::vector<int>' is not a valid type for a template non-type parameter because it is not structural
    3 | template<std::vector<int> vec = {1}>
      |                                   ^
In file included from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/vector:64,
                 from <source>:1:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/stl_vector.h:423:11: note: base class 'std::_Vector_base<int, std::allocator<int> >' is not public
  423 |     class vector : protected _Vector_base<_Tp, _Alloc>
      |           ^~~~~~
<source>: In function 'int main()':
<source>:17:24: error: request for member 'doSomething' in 'myTestClass', which is of non-class type 'int'
   17 |     return myTestClass.doSomething();

如何使用 vectors 执行此操作,甚至可能吗?而且,是否可以让 constexpr 成为会员?

你仍然(在 C++20 中,我认为 C++23 没有任何变化)不能使用 std::vector 作为 non-type 模板参数或者有任何标记为 constexprstd::vector 变量或具有导致 std::vector 作为值的任何常量表达式。

现在在 C++20 中允许但以前不允许的唯一用例是有一个(非constexprstd::vector变量或对象,它是在常量表达式在常量求值结束前被求值和销毁。

这意味着您现在可以使用函数

int f() {
    std::vector<int> vec;
    vec.push_back(3);
    vec.push_back(1);
    vec.push_back(2);
    std::sort(vec.begin(), vec.end());
    return vec.front();
}

在其上添加一个 constexpr 并在常量表达式中使用它,例如

static_assert(f() == 1);

但仅此而已。它仍然非常有用,因为之前你只能使用不需要任何动态内存分配的算法来计算 compile-time 的东西。这意味着您通常不能直接在 compile-time 上下文中使用通常的运行时 algorithm/implementation。

对于保留对动态分配内存的引用的任何类型也是如此。您需要在常量表达式评估期间销毁它们,即它们必须是函数中的临时变量或局部变量,或者 return 未存储在运行时上下文中的值。

在 non-type 模板参数的特定情况下,情况更加严格。并非所有可以作为 constexpr 变量的类型都可以用作 non-type 模板参数。有更严格的限制。它们必须是 so-called 结构类型 .

这些是例如算术类型、指针等基本类型。class 类型是 结构类型 如果它是 文字类型 并且也只有 non-static 数据成员,它们是 public 和非 mutable 以及所有这些成员,递归地, 结构类型 还有。

我认为 std::vector 显然不满足这些要求。 std::array 被明确指定为 结构类型 ,这就是为什么您可以将其用作 non-type 模板参数。