繁体   English   中英

静态断言 std::array 的大小,其类型是使用 decltype 从成员函数的返回值获得的

[英]Statically asserting the size of a std::array whose type is obtained using decltype from the return value of a member function

(抱歉标题笨拙;我不知道如何更简洁地总结这个问题。如果有人有更好的想法,请随时编辑!)

我想写一个自由函数,它可以根据类的成员函数的返回值自动确定其参数的类型。 使用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 是否有任何变化。

额外问题:有没有办法可以修改此代码以使其工作? 显然,如果decltype表达式不计算为std::array类型,则static_assert应该失败,因为size()成员函数不会是constexpr 我想有一个解决方案涉及添加模板辅助函数,但除非绝对必要,否则我宁愿不添加另一个函数定义。

我相信 clang 和其他人(icc 和 MSVC)在这里在技术上是正确的,而 GCC 是错误的。 static_assert 声明采用常量表达式[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");

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM