c++ - 静态断言 std::array 的大小,其类型是使用 decltype 从成员函数的返回值获得的
问题描述
(对于笨拙的标题表示歉意;我不知道如何更简洁地总结这个问题。如果有人有更好的想法,请随时编辑!)
我想写一个自由函数,它可以根据类的成员函数的返回值自动确定其参数的类型。使用decltype
,那部分很容易。
我还希望有一个编译时断言来验证关于该参数类型的假设,这就是我提出的解决方案失败的地方。
考虑以下 MCVE:
#include <type_traits>
#include <array>
#include <iostream>
class Foo
{
public:
std::array<int, 10> Get();
};
void PrintFoos(const decltype(Foo().Get())& param)
{
static_assert(param.size() == 10, "wrong size");
for (const auto& i : param)
{
std::cout << i << "\n";
}
}
GCC 可以很好地编译上面的代码,没有任何警告。
另一方面,Clang 抱怨道:
error: static_assert expression is not an integral constant expression
static_assert(param.size() == 10, "wrong size");
^~~~~~~~~~~~~~~~~~
MSVC 也是如此:
(13): error C2131: expression did not evaluate to a constant
(13): note: failure was caused by a read of a variable outside its lifetime
(13): note: see usage of 'param'
当其他编译器拒绝它时,为什么 GCC 编译得很好?是否有我从中受益的 GCC 扩展来支持这一点?
语言标准对此有何规定?我的目标是 C++17,但也想知道 C++14 是否有任何变化。
奖励问题:有没有办法可以修改此代码以使其工作?显然,如果表达式不计算类型,则static_assert
应该失败,因为成员函数不会是. 我想有一个解决方案涉及添加模板辅助函数,但除非绝对必要,否则我宁愿不添加另一个函数定义。decltype
std::array
size()
constexpr
解决方案
我相信 clang 和其他人(icc 和 MSVC)在这里在技术上是正确的,而 GCC 是错误的。static_assert-declaration采用常量表达式 [ expr.const ]/2。我相信手头案例的相关 C++17 措辞应该是[expr.const]/2.11:
一个表达式
e
是一个核心常量表达式,除非根据e
抽象机的规则,对 的求值将求值以下表达式之一:
- […]
- 一个id 表达式,它引用引用类型的变量或数据成员,除非引用具有前面的初始化并且
- 它用常量表达式初始化或
- 它的生命周期开始于
e
;- […]
但是,您static_assert
上面的表达式清楚地做到了这一点(param
是一个引用引用类型变量的id 表达式,没有任何例外适用)。因此,它不是一个常量表达式,并且程序是格式错误的[dcl.dcl]/6。C++14 标准中的相关措辞似乎是相同的。我会认为这是 GCC 中的一个错误。
如果您可以将函数更改为模板,则可以简单地推断出大小:
template <int N>
void PrintFoos(const std::array<int, N>& param)
{
…
}
或者,如果你想让一切都依赖于Foo
,你也可以定义一个公共常量并从中派生数组类型等:
class Foo
{
public:
static constexpr auto size = 10;
std::array<int, size> Get();
};
void PrintFoos(const decltype(Foo().Get())& param)
{
static_assert(Foo::size == 10, "wrong size");
}
当然,您可以使用帮助模板:
template <typename T>
constexpr std::size_t deduce_array_size = 0U;
template <typename T, std::size_t N>
constexpr std::size_t deduce_array_size<std::array<T, N>> = N;
template <typename T>
constexpr std::size_t deduce_array_size<T&> = deduce_array_size<T>;
template <typename T>
constexpr std::size_t deduce_array_size<T&&> = deduce_array_size<T>;
接着
void PrintFoos(const decltype(Foo().Get())& param)
{
static_assert(deduce_array_size<decltype(param)> == 10, "wrong size");
}
最后,另一种选择(受 Yakk - Adam Nevraumont 下面的评论启发)是在常量表达式中简单地创建数组类型的纯右值并询问它的大小:
static_assert(std::decay_t<decltype(param)>{}.size() == 10, "wrong size");
推荐阅读
- java - Java中数组的索引可以是负数吗?
- spring - Spring + Kotlin - 是否可以检测某些函数调用?
- python - Pandas:如何仅在一列中更改元素的类型?
- spring-boot - 在 Project reactor 中重复请求的最佳方式
- html - 如何为几种字体类型之一使用链接预加载
- javascript - 我为事件监听器生成了一个构造函数。但它没有按我想要的方式工作
- redis - RedisTemplate 执行 get 与 multiget 的性能差异
- c# - 责任链“恰好一个处理程序”
- python-3.x - Ubuntu mate 18.04 - Kivy 应用程序可以在桌面上运行,但不能在 cli 中运行(无法找到任何有价值的 Windows 提供程序)
- sql-server - VB6 10多个存储过程参数