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
我相信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)