我正在做一个继承自std :: array的简单类。关键是,如果下标运算符用于越界索引,它将引发编译时错误。但是,我一直收到错误消息。这是简化的代码。
#include <array>
using namespace std;
template<typename type, size_t size>
struct container : array<type,size>
{
constexpr inline type& operator[](int index) const
{
static_assert(index<size,"");
return ((static_cast<const array<type,size> >(*this))[index]);
}
template<class... bracelist>
constexpr container(bracelist&&... B)
:array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[2];
}
它给我的错误是:
main.cpp|80|error: non-constant condition for static assertion
main.cpp|80|error: 'index' is not a constant expression
但是,我在return语句中使用了“ index”,并且注释掉static_assert使其正常工作。如果index不是一个常数表达式,在static_cast之后,我是否不能在std :: array的下标运算符中使用它?我是使用constexpr功能的新手,因此可以提供任何帮助。谢谢。
注意:我知道std :: array的constexpr下标运算符已经做到了,我只想知道如何做以备将来使用。谢谢。
答案 0 :(得分:2)
constexpr函数有2个真正有用的功能,它们的相互作用并不总是被人们完全理解。
在constexpr上下文中,它们仅评估为constexpr参数采用的代码路径。
在非constexpr上下文中,它们的行为与常规函数完全相同。
这意味着我们可以使用异常来产生很大的效果。
由于在constexpr上下文中,如果采用了异常路径,则这是编译器错误(在constexpr上下文中不允许抛出)。您会在编译器的错误输出中看到“异常”。
示例:
#include <array>
#include <stdexcept>
template<typename type, std::size_t size>
struct container : std::array<type,size>
{
constexpr auto operator[](std::size_t index) const
-> type const&
{
if (index < size)
return static_cast<const std::array<type,size>>(*this)[index];
else
throw std::out_of_range("index out of range" + std::to_string(index));
}
template<class... bracelist>
constexpr container(bracelist&&... B)
: std::array<type,size>{std::forward<bracelist>(B)...}
{}
container() = default;
};
int main()
{
constexpr container<int,4> myarray = {5,6,7,8};
constexpr int number = myarray[4];
}
示例输出:
main.cpp: In function 'int main()':
main.cpp:28:37: in 'constexpr' expansion of 'myarray.container<int, 4>::operator[](4)'
main.cpp:13:81: error: expression '<throw-expression>' is not a constant expression
throw std::out_of_range("index out of range" + std::to_string(index));
这种方法实际上比static_assert更具通用性,因为它在编译和运行时均有效。
答案 1 :(得分:1)
您要记住的是,constexpr
函数可以在运行时使用非constexpr参数进行调用。 constexpr
对于一个函数而言,意味着该函数在编译时评估的表达式(例如,另一个constexpr或模板参数)中可以可用,但不能排他地使用。 constexpr
函数仍可以以经典的方式调用,即在运行时使用运行时变量。这意味着constexpr
函数的参数不能并且不是编译时常量。
这不适用于您的情况,但是通常来说,如果您知道总是使用编译时间常数来调用参数,而不是将其设为模板参数,就可以了。
constexpr void foo(int a)
{
static_assert(a != 0); // illegal code because the parameter
// cannot be a compile time constant
}
void test()
{
int x;
std::cin >> x;
foo(x); // this is perfectly legal
}
template <int N>
void foo()
{
static_assert(N != 0); // legal
}
void test()
{
int x;
std::cin >> x;
foo<x>(); // illegal, x is not a compile time constant
foo<24>(); // legal
constexpr int c = 11;
foo<c>();; // legal
}
答案 2 :(得分:0)
这是std::get<N>(array)
的原因-这是确保以符合语言规则的方式肯定地传递“编译时值”的唯一方法。您尝试创建编译时op[]
的操作无效。您当然可以制作自己的模板访问器,例如std::get
,但有人可能会问为什么不直接使用std::array
。