(涉及到我的另一个问题;如果你也看看这个问题,我将非常感激。)
std::array<T,N>::size
是constexpr
,那么为什么下面的代码甚至无法编译?
#include <array>
#include <iostream>
constexpr auto print_size = [](auto const& array){
constexpr auto size = array.size();
std::cout << size << '\n';
};
int main() {
print_size(std::array<int,3>{{1,2,3}});
}
错误如下:
$ g++ -std=c++17 deleteme.cpp && ./a.out
deleteme.cpp: In instantiation of ‘<lambda(const auto:1&)> [with auto:1 = std::array<int, 3>]’:
deleteme.cpp:10:42: required from here
deleteme.cpp:5:20: error: ‘array’ is not a constant expression
5 | constexpr auto size = array.size();
| ^~~~
但我想知道为什么。
在 lambda 调用站点,参数在编译时已知,并且 lambda 应该使用等于
auto
的 std::array<int,3>
进行实例化,其中 3
是编译时值,因此应该是 array.size()
的输出.
我的推理有什么问题吗?
5 - 表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项: [...]
- (5.12) 引用引用类型的变量或数据成员的 id 表达式,除非引用具有前面的初始化并且
- (5.12.1) 它可用于常量表达式或
- (5.12.2)其生命周期开始于E的评估内;
由于变量
array
是一个引用,因此不允许对其求值(在表达式 array.size()
内),即使求值实际上没有执行任何操作。
按值传递
array
(const
或非 const
)使代码有效:
constexpr auto print_size = [](auto const array){
constexpr auto size = array.size(); // ok
std::cout << size << '\n';
};
但是引用该参数并在下一行使用它是无效的:
constexpr auto print_size = [](auto const arr){
auto const& array = arr;
constexpr auto size = array.size(); // error
std::cout << size << '\n';
};
请注意,gcc 9 错误地接受此代码;只是从版本 10 开始,gcc 才得到了正确的结果。
gcc 10 在相关领域仍然不合规;它接受在引用上调用
static constexpr
成员函数。 使用引用的 constexpr 静态成员作为模板参数这是不正确的,clang 正确地拒绝它:
struct S { static constexpr int g() { return 1; } };
void f(auto const& s) {
constexpr auto x = s.g(); // error
constexpr auto y = decltype(s)::g(); // ok
}
int main() { f(S{}); }
附录:根据论文P2280R1,这将来可能会改变。
我正在观看 2014 使用 Boost.Hana 进行元编程:统一 Boost.Fusion 和 Boost.MPL 演示文稿,其中 Louise Dionne 谈到了这个主题并解释了 @super 在评论中告诉我的内容,但我不明白。 .
这是我对这个概念的重新表述:不存在
constexpr
函数参数这样的东西,因此每当 lambda(实际上是其底层 operator()
)为给定类型的 array
实例化时,那个 single 实例化 应该适用于该类型的 constexpr
和非
constexpr
参数。正如 Louis Dionne 在链接的演示中所说,
[...] 如果函数依赖于参数,则无法在函数内生成这提供了解决该问题的方法。使用
constexpr
[...] 函数的返回类型可能仅取决于其参数的类型,而不取决于它们的值 [...]
array
的类型而不使用
array
的值:
constexpr auto print_size = [](auto const& array){
using array_type = decltype(array);
constexpr auto size = array_type{}.size();
std::cout << size << '\n';
};
我认为这本质上与@Jarod42 在评论中建议的没有什么不同:
您可能会使用作为补充,我又玩了一些,因为最后一件事困扰着我:
constexpr auto size = std::tuple_size<std::decay_t<decltype(array)>>::value
std::array
的大小不是值的一部分,而是type的一部分,所以为什么我不能调用
size
中的contexpr
成员函数?原因是
std::array<T,N>::size()
遗憾的是不是
static
。如果是的话,可以像下面的注释行那样称呼它(struct A
用于比较):
#include <array>
#include <iostream>
#include <type_traits>
template<std::size_t N>
struct A {
static constexpr std::size_t size() noexcept { return N; }
};
constexpr auto print_size = [](auto const& array){
constexpr auto size = std::decay_t<decltype(array)>::size();
std::cout << size << '\n';
};
int main() {
//print_size(std::array<int,3>{{1,2,3}});
print_size(A<3>{});
}