【问题标题】:Using a `constexpr` function on a variable passed as lvalue in a non-constexpr function在非 constexpr 函数中作为左值传递的变量上使用“constexpr”函数
【发布时间】:2020-06-23 13:13:07
【问题描述】:

我使用std::array 作为在编译时表示具有固定长度的向量的基础,并希望使用std::array::size 作为constexpr 函数来禁用1D 和@987654328 的叉积计算@ 向量。

当我在非 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);
}

我发现 this question 提到了 GCC 8.0 中的一个错误,但我使用的是 g++ (GCC) 10.1.0

引用the answer

表达式 e 是一个核心常量表达式,除非 e,遵循抽象机(6.8.1)的规则,将评估 以下表达式之一:

... 一个 id 表达式,它引用一个变量或数据成员 引用类型,除非该引用具有前面的初始化和 要么是用常量表达式初始化,要么是它的生命周期 开始内评估e

这是否意味着,在人类(非标准)语言中,在我的表达式 e:=cross(p1,p2)p1p2 之前初始化为 constexpr 及其生命周期没有以e开头,所以即使p1p2是一种数据类型的对象,其大小在编译时是已知的,其mfunction sizeconstexpr mfunction,我现在必须将它们声明为 constexpr,然后才能将它们绑定为不是 constexpr 的函数中的左值?

【问题讨论】:

  • 这不是特别小,问题标题谈到if constexpr,但在这个例子中没有if constexpr
  • 嗯,tl;博士。但是,您可以将 static constexpr size_t size() { return D; } 添加到您的 Vector 并执行 return cross_dispatch&lt;VectorType::size(), VectorType&gt;::apply(vec1, vec2); 以解决此特定问题。
  • 我编辑了标题,有另一个带有if constexpr 的函数模板,但我最终使用了crossVector 是否也没有从 std::array 继承 static constexpr size_t size(),还是我忘记了另一条规则?
  • std::array::size() 真的是静态的吗?我不认为它是 - 即使是,我认为通过实例 (std::size(vec1)) 调用它也行不通,你必须像 VectorType::size() 那样调用它
  • 你会对这篇博文感兴趣The constexpr array size problem,作者是第一个评论者! @巴里

标签: c++ constexpr c++20 if-constexpr


【解决方案1】:

下面,我回答为什么您的代码不起作用。专注于您的用例:正如其他人所说,std::array::size 不是static,所有std::size 所做的只是调用该非静态函数。您最好的选择是简单地将 static 大小函数添加到您的 Vector 类:

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>();
}

因为p1q1 是静态的,所以它们的地址可以在编译时知道,从而可以初始化cross 的模板。模板参数在运行时不会改变,所以std::size(vec1) 现在是一个常量表达式。

【讨论】:

    猜你喜欢
    • 2020-10-08
    • 2021-04-20
    • 1970-01-01
    • 2013-12-24
    • 2018-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多