在非 constexpr 函数中作为左值传递的变量上使用 `constexpr` 函数
Using a `constexpr` function on a variable passed as lvalue in a non-constexpr function
我正在使用 std::array
作为表示在编译时具有固定长度的向量的基础,并且想使用 std::array::size
作为 constexpr
函数来禁用交叉计算1D
和 2D
向量的乘积。
当我在非 constexpr 函数中使用 std::array::size
时,它将我的向量作为左值参数,我得到一个错误:
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
。
引用
An expression e is a core constant expression unless the evaluation of
e, following the rules of the abstract machine (6.8.1), would evaluate
one of the following expressions:
... an id-expression that refers to a variable or data member of
reference type unless the reference has a preceding initialization and
either it is initialized with a constant expression or its lifetime
began within the evaluation of e
这是否意味着,在人类(非标准)语言中,在我的表达式中 e:=cross(p1,p2)
、p1
和 p2
是 而不是 之前初始化为 constexpr
并且它们的生命周期 没有 以 e
开头,所以即使 p1
和 p2
是一个对象其大小在编译时已知的数据类型 nad 其 mfunction size
是 a constexpr
mfunction,我现在必须在将它们绑定为左值之前将它们声明为 constexpr不是 constexpr
?
的函数
下面,我回答为什么你的代码不起作用。专注于您的用例:正如其他人所说, std::array::size
is not static
, and all std::size
所做的就是调用该非静态函数。你最好的选择是简单地添加一个 static
大小函数到你的 Vector
class:
static constexpr auto size() {
return D;
}
您的 cross
实现将不起作用,因为您不能使用非常量表达式来初始化模板。请参阅 this SO answer 为什么函数参数不是常量表达式。
基本上,调用您的 cross
函数需要为 std::size(vec1)
的每个不同值生成一个 cross_dispatch
结构的新实例,这还需要知道 地址 在编译时每个给定的 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
的模板被初始化。模板参数在 运行 时不会更改,因此 std::size(vec1)
现在是常量表达式。
我正在使用 std::array
作为表示在编译时具有固定长度的向量的基础,并且想使用 std::array::size
作为 constexpr
函数来禁用交叉计算1D
和 2D
向量的乘积。
当我在非 constexpr 函数中使用 std::array::size
时,它将我的向量作为左值参数,我得到一个错误:
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);
}
我发现 g++ (GCC) 10.1.0
。
引用
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.8.1), would evaluate one of the following expressions:
... an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either it is initialized with a constant expression or its lifetime began within the evaluation of e
这是否意味着,在人类(非标准)语言中,在我的表达式中 e:=cross(p1,p2)
、p1
和 p2
是 而不是 之前初始化为 constexpr
并且它们的生命周期 没有 以 e
开头,所以即使 p1
和 p2
是一个对象其大小在编译时已知的数据类型 nad 其 mfunction size
是 a constexpr
mfunction,我现在必须在将它们绑定为左值之前将它们声明为 constexpr不是 constexpr
?
下面,我回答为什么你的代码不起作用。专注于您的用例:正如其他人所说, std::array::size
is not static
, and all std::size
所做的就是调用该非静态函数。你最好的选择是简单地添加一个 static
大小函数到你的 Vector
class:
static constexpr auto size() {
return D;
}
您的 cross
实现将不起作用,因为您不能使用非常量表达式来初始化模板。请参阅 this SO answer 为什么函数参数不是常量表达式。
基本上,调用您的 cross
函数需要为 std::size(vec1)
的每个不同值生成一个 cross_dispatch
结构的新实例,这还需要知道 地址 在编译时每个给定的 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
的模板被初始化。模板参数在 运行 时不会更改,因此 std::size(vec1)
现在是常量表达式。