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

Cod*_*ray -2 c++ static-assert decltype language-lawyer

(为标题笨拙而道歉;我不知道如何更简洁地概括这个问题。如果有人有更好的主意,请随时进行编辑!)

我想编写一个自由函数,该函数可以基于类的成员函数的返回值自动确定其参数的类型。使用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";
    }
}
Run Code Online (Sandbox Code Playgroud)

GCC可以很好地编译以上代码,但不会发出警告。

另一方面,lang声抓住:

error: static_assert expression is not an integral constant expression
    static_assert(param.size() == 10, "wrong size");
                  ^~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)

当其他编译器拒绝它时,为什么GCC会对此进行编译?我可以从GCC扩展中受益吗?

语言标准对此要说些什么?我的目标是C ++ 17,但也希望了解C ++ 14是否有任何更改。

奖励问题:有什么方法可以修改此代码以使其起作用?显然,如果表达式的值不等于类型,则static_assert 将会失败,因为成员函数不会为。我想有一个解决方案,其中涉及添加模板辅助函数,但是除非绝对必要,否则我不愿添加其他函数定义。decltypestd::arraysize()constexpr

Mic*_*zel 5

我相信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)
{
    …
}
Run Code Online (Sandbox Code Playgroud)

另外,如果您想使所有内容都依赖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");
}
Run Code Online (Sandbox Code Playgroud)

而且,当然,您可以使用帮助程序模板:

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>;
Run Code Online (Sandbox Code Playgroud)

然后

void PrintFoos(const decltype(Foo().Get())& param)
{
    static_assert(deduce_array_size<decltype(param)> == 10, "wrong size");
}
Run Code Online (Sandbox Code Playgroud)

最后,另一种选择(受到Yakk-Adam Nevraumont的评论的启发)将是在常量表达式中简单地创建数组类型的prvalue并要求其大小:

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