c++ - 在非 constexpr 函数中作为左值传递的变量上使用“constexpr”函数
问题描述
我std::array
用作表示在编译时具有固定长度的向量的基础,并希望std::array::size
用作一个函数来禁用和向量constexpr
的叉积计算。1D
2D
当我std::array::size
在非 constexpr 函数中使用时,它将我的向量作为左值参数,我得到一个错误:
main.cpp: In instantiation of ‘VectorType cross(const VectorType&, const VectorType&) [with VectorType = Vector<double, 3>]’:
main.cpp:97:16: required from here
main.cpp:89:62: error: ‘vec1’ is not a constant expression
89 | return cross_dispatch<std::size(vec1), VectorType>::apply(vec1, vec2);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
main.cpp:89:36: note: in template argument for type ‘long unsigned int’
89 | return cross_dispatch<std::size(vec1), VectorType>::apply(vec1, vec2);
|
这是该main
函数的最小工作示例:
#include <array>
#include <iostream>
using namespace std;
template<typename AT, auto D>
class Vector final
:
public std::array<AT, D>
{
public:
using container_type = std::array<AT,D>;
using container_type::container_type;
template<typename ... Args>
constexpr Vector(Args&& ... args)
:
container_type{std::forward<Args>(args)...}
{}
// Delete new operator to prevent undefined behavior for
// std::array*= new Vector; delete array; std::array has
// no virtual destructors.
template<typename ...Args>
void* operator new (size_t, Args...) = delete;
};
using vector = Vector<double, 3>;
template<std::size_t DIM, typename VectorType>
struct cross_dispatch
{
static VectorType apply(VectorType const& v1, VectorType const& v2)
{
static_assert(std::size(v1) < 3, "Cross product not implemented for 2D and 1D vectors.");
static_assert(std::size(v1) > 3, "Cross product not implemented for ND vectors.");
return VectorType();
}
};
template<typename VectorType>
struct cross_dispatch<3, VectorType>
{
static VectorType apply(VectorType const& v1, VectorType const& v2)
{
return VectorType(v1[1]*v2[2] - v1[2]*v2[1],
v1[2]*v2[0] - v1[0]*v2[2],
v1[0]*v2[1] - v1[1]*v2[0]);
}
};
template <typename VectorType>
VectorType cross(VectorType const& vec1, VectorType const& vec2)
{
return cross_dispatch<std::size(vec1), VectorType>::apply(vec1, vec2);
}
int main()
{
vector p1 {1.,2.,3.};
vector q1 {1.,2.,3.};
cross(p1,q1);
}
我发现这个问题提到了 GCC 8.0 中的一个错误,但我使用的是g++ (GCC) 10.1.0
.
引用答案
表达式 e 是核心常量表达式,除非按照抽象机 (6.8.1) 的规则对 e 的求值将求值以下表达式之一:
... 一个 id 表达式,它引用引用类型的变量或数据成员,除非该引用具有前面的初始化,并且它是用常量表达式初始化的,或者它的生命周期在 e 的计算中开始
这是否意味着,在人类(非标准)语言中,在我的表达式e:=cross(p1,p2)
中,p1
并且p2
在as 之前没有初始化,constexpr
并且它们的生命周期不是以 开头e
,所以即使p1
andp2
是在编译时知道其大小的数据类型的对象 nad谁的 mfunctionsize
是constexpr
mfunction,我现在必须将它们声明为 constexpr,然后才能将它们绑定为不是的函数中的左值constexpr
?
解决方案
下面,我回答为什么您的代码不起作用。专注于您的用例:正如其他人所说的那样,std::array::size
is not static
,并且std::size
所做的只是调用该非静态函数。你最好的选择是简单地static
为你的类添加一个大小函数Vector
:
static constexpr auto size() {
return D;
}
您的实现cross
将不起作用,因为您不能使用非常量表达式来初始化模板。有关为什么函数参数不是常量表达式,请参阅此 SO 答案。
基本上,调用你的函数需要为每个不同的值cross
生成一个新的结构实例,这也需要在编译时知道每个给定的地址,因为调用了一个非静态函数。您应该能够从中看出编译器根本无法知道需要创建哪些实例。cross_dispatch
std::size(vec1)
vec1
std::size
cross_dispatch
上面,我提供了一个特定于您的用例的解决方案。如果您所做的不仅仅是测量 的大小Vector
,则第二种解决方案将涉及将对象作为模板参数传递(这将要求它们是static
):
template <typename VectorType, VectorType const& vec1, VectorType const& vec2>
constexpr VectorType cross()
{
return cross_dispatch<std::size(vec1), VectorType>::apply(vec1, vec2);
}
int main()
{
static vector p1 {1.,2.,3.};
static vector q1 {1.,2.,3.};
cross<vector, p1, q1>();
}
因为p1
和q1
是静态的,所以它们的地址可以在编译时知道,从而cross
可以初始化 's 模板。模板参数在运行时不会改变,所以std::size(vec1)
现在是一个常量表达式。
推荐阅读
- reactjs - 如何使用 React 加入状态中的多个 Apollo 查询?
- c++ - 为什么这个 C++ 包装类没有被内联?
- angular - 如何使组件路径成为Angular 6中的根URL
- google-apps-script - 带有显示名称的 googe-apps-script GoogleSheets 下拉列表
- python - 列表中具有相同实例数的 Python 最大值
- html - 如何在手机和平板电脑上制作一个旋转木马,在扩展 md 时消失
- php - 使用多个参数搜索,LIKE、ORDER BY
- python - (Numpy C API)迭代单个数组:NpyIter 与 for 循环(使用 PyArray_DATA)
- ruby-on-rails - 如何在 Rails 5 中验证相同操作的不同 ActiveRecord
- javascript - 反斜杠拆分合并拆分元素