C++ 静态断言std::数组的大小,该数组的类型是使用成员函数返回值中的decltype获得的

C++ 静态断言std::数组的大小,该数组的类型是使用成员函数返回值中的decltype获得的,c++,language-lawyer,decltype,static-assert,C++,Language Lawyer,Decltype,Static Assert,为这个笨拙的标题道歉;我不知道如何更简洁地概括这个问题。如果有人有更好的想法,请随时编辑 我想写一个自由函数,它可以根据类的成员函数的返回值自动确定其参数的类型。使用decltype,这一部分很简单 我还希望有一个编译时断言来验证关于该参数类型的假设,这就是我提出的解决方案的不足之处 考虑以下MCVE: 包括 包括 包括 福班 { 公众: std::数组Get; }; void PrintFoosconst decltypeFoo.Get和param { static_assertparam.s

为这个笨拙的标题道歉;我不知道如何更简洁地概括这个问题。如果有人有更好的想法,请随时编辑

我想写一个自由函数,它可以根据类的成员函数的返回值自动确定其参数的类型。使用decltype,这一部分很简单

我还希望有一个编译时断言来验证关于该参数类型的假设,这就是我提出的解决方案的不足之处

考虑以下MCVE:

包括 包括 包括 福班 { 公众: std::数组Get; }; void PrintFoosconst decltypeFoo.Get和param { static_assertparam.size==10,大小错误; 对于常量自动&i:param {
std::cout我认为clang和其他icc和MSVC在技术上是正确的,GCC是错误的。A采用一个常量表达式。我认为与本案相关的C++17措辞应该是:

表达式e是一个核心常量表达式,除非根据抽象机器的规则对e进行求值时会对以下表达式之一求值:

[…] 一种id表达式,它引用引用类型为引用的变量或数据成员,除非该引用具有前面的初始化,并且 它是用常量表达式或 其寿命开始于e的评估范围内; […]

上面的表达式中的表达式显然是正确的,但是PARAM是一个ID表达式,它引用了引用类型的变量,没有例外应用。因此,它不是常数表达式,程序不正确。C++ 14标准中的相关措辞似乎是相同的。海湾合作委员会

如果可以将函数更改为模板,则可以简单地推断大小:

template <int N>
void PrintFoos(const std::array<int, N>& param)
{
    …
}
当然,您也可以使用帮助器模板:

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的评论启发的另一个选项是,在常量表达式中创建数组类型的prvalue,并询问其大小:

static_assert(std::decay_t<decltype(param)>{}.size() == 10, "wrong size");

也许这与std::array::size的定义方式有关。size是一个成员函数,所以如果param是constexpr,那么param.size是constexpr,而param不是。可能GCC将size定义为静态,这样param不需要是constexpr,param.size就可以是constexpr.Nope。不是这样。我刚刚检查了libc++和libstdC++,我不确定,但我认为这可能是一个GCC bug。它真的必须是DeCytFoo吗?GET?如果你可以只使用一个STD:::数组,你可以从参数类型推断……数组的大小……米迦勒,这就需要我硬编码STD::那个函数的签名中的数组长度:我不想做。我只想主在类接口的一个地方,而不是在自由函数中。换句话说,它不是干的。我有点困惑。你说你不想在两个不同的地方保持大小,但你的问题是,这个静态断言将大小与你现在说不想维护的地方的硬编码值进行比较a作为硬编码值的大小!?如果数组类型已经从Foo所做的事情派生出来,那么静态断言有什么必要呢?它不可能不是Foo::Get返回的东西的大小,因为如果它是,那么函数将永远不会被调用!?对我来说,这是标准中的一个缺陷;类型为std::decay\u t c的变量一个have.size调用它没有问题,因为.size并不需要它的数组对象…@Yakk AdamNevraumont公平地说,我同意你的观点,这是标准目前所说的,但是…除非我忽略了什么,当然…谢谢,这是一个奇妙的答案,基本上也是我一直在寻找的。我担心我的andards ese相当弱,因此我对您和Adam的观察感到有点困惑,即问题实际上在于表达式是引用。如果是这样,为什么在执行静态_assertparams.size==10时将参数的类型更改为const std::array¶m不会产生问题?在这种情况下,它仍然是一个引用。问题是什么decltype的使用与显式类型规范之间的区别?@CodyGray如果我将其从const decltypeFoo.Get¶m更改为const std::array¶m,我会在clang中得到完全相同的错误消息。我应该这样做,因为这两个版本之间绝对没有区别。在这两种情况下,类型完全相同。
static_assert(std::decay_t<decltype(param)>{}.size() == 10, "wrong size");