I am using std::array
as the base for representing vectors that have fixed length at compile time, and want to use std::array::size
as a constexpr
function to disable the calculation of cross product for 1D
and 2D
vectors.
When I use std::array::size
in a non-constexpr function, that takes on my vectors as lvalue arguments, I get an error:
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);
|
Here is the minimal working example with the main
function:
#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);
}
I found this question that mentions a bug in GCC 8.0, but I am using g++ (GCC) 10.1.0
.
To quote the answer
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
Does this mean, in human (non-standard) language, that in my expression e:=cross(p1,p2)
, p1
and p2
are not initialized before as constexpr
and their lifetime did not begin with e
, so even though p1
and p2
are objects of a data type whose size is known at compile time nad whose mfunction size
is a constexpr
mfunction, I now have to declare them as constexpr before binding them as lvalues in a function that's not constexpr
?