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

问题描述 投票:-1回答:1

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

我想编写一个自由函数,它可以根据类的成员函数的返回值自动确定其参数的类型。使用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会编译好呢?是否有一个GCC扩展,我受益于支持这个?

语言标准对此有何看法?我的目标是C ++ 17,但也有兴趣知道C ++ 14是否有任何变化。

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

c++ language-lawyer decltype static-assert
1个回答
5
投票

我相信clang和其他人(icc和MSVC)在技术上是正确的,GCC是错误的。一个static_assert-declaration采取恒定表达[expr.const]/2。我相信手头的案例的相关C ++ 17措辞应该是[expr.const]/2.11

表达式e是核心常量表达式,除非根据抽象机器的规则评估e将评估以下表达式之一:

  • […]
  • 一个id-expression,引用引用类型的变量或数据成员,除非引用具有先前的初始化和任何一个 它是用常量表达式初始化的 它的生命始于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)将简单地在常量表达式中创建数组类型的prvalue,并询问其大小:

static_assert(std::decay_t<decltype(param)>{}.size() == 10, "wrong size");
© www.soinside.com 2019 - 2024. All rights reserved.