使用参数的 constexpr 运算符重载问题
constexpr operator overloading issues with using arguments
我正在制作一个简单的 class 继承自 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 下标运算符已经这样做了,我只是想知道如何做以供将来使用。谢谢
您必须牢记的是,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
}
这就是 std::get<N>(array)
的原因 — 这是确保以满足语言规则的方式传递 "compile-time value" 的唯一方法。您创建编译时 op[]
的尝试无法正常工作。您当然可以制作自己的模板化访问器,例如 std::get
,但有人可能会问为什么不直接使用 std::array
。
constexpr 函数有 2 个非常有用的特性,它们之间的相互作用并不总是被充分理解。
在 constexpr 上下文中,它们仅评估为 constexpr 参数采用的代码路径。
在非 constexpr 上下文中,它们的行为与常规函数完全一样。
这意味着我们可以使用异常来达到很好的效果。
因为在 constexpr 上下文中,如果采用异常路径,这是一个编译器错误(在 constexpr 上下文中不允许抛出)。您会在编译器的错误输出中看到 "exception"。
示例:
#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 更通用,因为它在编译和运行时都有效。
我正在制作一个简单的 class 继承自 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 下标运算符已经这样做了,我只是想知道如何做以供将来使用。谢谢
您必须牢记的是,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
}
这就是 std::get<N>(array)
的原因 — 这是确保以满足语言规则的方式传递 "compile-time value" 的唯一方法。您创建编译时 op[]
的尝试无法正常工作。您当然可以制作自己的模板化访问器,例如 std::get
,但有人可能会问为什么不直接使用 std::array
。
constexpr 函数有 2 个非常有用的特性,它们之间的相互作用并不总是被充分理解。
在 constexpr 上下文中,它们仅评估为 constexpr 参数采用的代码路径。
在非 constexpr 上下文中,它们的行为与常规函数完全一样。
这意味着我们可以使用异常来达到很好的效果。
因为在 constexpr 上下文中,如果采用异常路径,这是一个编译器错误(在 constexpr 上下文中不允许抛出)。您会在编译器的错误输出中看到 "exception"。
示例:
#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 更通用,因为它在编译和运行时都有效。