Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++;运算符修改/元编程策略,以减少冗长的语法 我现在正在学习C++中的模板元编程和表达式模板,所以作为练习,我正在创建一个线性代数库来实践我正在学习的概念。_C++_Operator Overloading_Template Meta Programming_Expression Templates - Fatal编程技术网

C++;运算符修改/元编程策略,以减少冗长的语法 我现在正在学习C++中的模板元编程和表达式模板,所以作为练习,我正在创建一个线性代数库来实践我正在学习的概念。

C++;运算符修改/元编程策略,以减少冗长的语法 我现在正在学习C++中的模板元编程和表达式模板,所以作为练习,我正在创建一个线性代数库来实践我正在学习的概念。,c++,operator-overloading,template-meta-programming,expression-templates,C++,Operator Overloading,Template Meta Programming,Expression Templates,到目前为止,我的库为所有可以重载的二进制运算符提供了一个完整的非成员运算符重载列表,并且有一个相当灵活的接口,易于扩展。然而,我遇到的一个问题是,矩阵运算通常有多种变体。例如,对于乘法,有一般矩阵乘法、点积、kroenecker积、hadamard积、叉积等等 在Matlab中使用的一种巧妙方法是用于hadamard乘法(and.^,./等)的.*运算符。在这种情况下,Matlab语言使用。运算符作为*运算符的修饰符。然而,我不知道C++语言中允许操作员像这样修改的任何机制。这种行为有没有干净的

到目前为止,我的库为所有可以重载的二进制运算符提供了一个完整的非成员运算符重载列表,并且有一个相当灵活的接口,易于扩展。然而,我遇到的一个问题是,矩阵运算通常有多种变体。例如,对于乘法,有一般矩阵乘法、点积、kroenecker积、hadamard积、叉积等等

在Matlab中使用的一种巧妙方法是用于hadamard乘法(and.^,./等)的.*运算符。在这种情况下,Matlab语言使用。运算符作为*运算符的修饰符。然而,我不知道C++语言中允许操作员像这样修改的任何机制。这种行为有没有干净的解决办法

以下是我已经考虑过的一些事情:

  • 运算符重载允许使用额外的模板参数。然而,我不完全确定如何在这种情况下利用这一点。例如,一些可能很好的东西(尽管在实践中,我不确定是否有有效的语法来实现这一点):
  • 创建一个自由二进制函数。可能的语法:

所有这些似乎都没有Matlab的语法那么优雅:

mat1 .* mat2;

<> P>有什么技术可以接近操作员修改语法,我可以考虑吗?或者使用任何通用技术来减少使用语法的冗长程度?如果不是,有什么概念可以包含在C++的未来版本中,这在这里可能有用吗?< /p> < p>这里是我认识到的:

  • 可以支持多个语法。我不需要选择一种,我只需要实现一种方法,并在顶部添加抽象,如果需要的话,模仿其他语法
  • Hadamard乘法(和除法等)是数组的默认乘法方式,而GEMM是矩阵的默认乘法方式
  • 对于这两项,我选择实现一个n维数组类(因为实现中使用了CRTP基类和自由函数,所以这只是提供一个带有必要特征/别名声明的空结构的问题)

    以下情况可能导致阿达玛乘法:

  • 矩阵A的维数与矩阵B相同,并且两个矩阵都不是方矩阵,因此很明显gemm无效,但hadamard乘法无效。因此,使用概念覆盖此行为是有意义的。换言之:

    // Results in hadamard multiplication
    template<object_type lhs_t, object_type rhs_t> requires 
    (is_same_dimensions_v<lhs_t, rhs_t> && !is_special_mult_supported_v<lhs_t, rhs_t>)
    constexpr auto operator*(lhs_t&& lhs, rhs_t&& rhs) noexcept -> mul_expr<lhs_t, rhs_t>
    {
        return { std::forward<lhs_t>(lhs), std::forward<rhs_t>(rhs) };
    }
    
    // Results in general matrix multiplication
    template<typename lhs_t, typename rhs_t> requires is_gemm_supported_v<lhs_t, rhs_t>
    constexpr auto operator*(lhs_t&& lhs, rhs_t&& rhs) noexcept -> gemm_expr<lhs_t, rhs_t>
    {
        return { std::forward<lhs_t>(lhs), std::forward<rhs_t>(rhs) };
    }
    
  • Gemm表达式可以显式转换为hadamard表达式

    // explicitly create a hadamard multiplication expression
    auto c = static_cast<mul_expr>(a * b);
    
  • 有了这些规则,就可以添加api级别的抽象以保持清晰:

        // c = hadamard(a * b);
        template<expr_type expr_t> requires std::is_convertible_v<std::decay_t<expr_t>, mul_expr>
        constexpr auto hadamard(expr_t&& expr) noexcept
        {
            return static_cast<mul_expr>(expr);
        }
    
        // support c = hadamard_mult(a, b); syntax
        template<object_type lhs_t, object_type rhs_t> requires is_same_dimensions_v<lhs_t, rhs_t>
        constexpr auto hadamard_mult(lhs_t&& lhs, rhs_t&& rhs) noexcept
        {
            return hadamard(lhs * rhs);
        }
    
    可以(也应该)导致阿达玛乘法。此外,对类型系统的简单操作很容易导致函数语法容易支持的情况


    非常感谢你们在上面的评论中帮助我集思广益并得出这个结论。通过这里的讨论,我对我的图书馆非常满意。

    以下是我对这一点的认识:

  • 可以支持多个语法。我不需要选择一种,我只需要实现一种方法,并在顶部添加抽象,如果需要的话,模仿其他语法
  • Hadamard乘法(和除法等)是数组的默认乘法方式,而GEMM是矩阵的默认乘法方式
  • 对于这两项,我选择实现一个n维数组类(因为实现中使用了CRTP基类和自由函数,所以这只是提供一个带有必要特征/别名声明的空结构的问题)

    以下情况可能导致阿达玛乘法:

  • 矩阵A的维数与矩阵B相同,并且两个矩阵都不是方矩阵,因此很明显gemm无效,但hadamard乘法无效。因此,使用概念覆盖此行为是有意义的。换言之:

    // Results in hadamard multiplication
    template<object_type lhs_t, object_type rhs_t> requires 
    (is_same_dimensions_v<lhs_t, rhs_t> && !is_special_mult_supported_v<lhs_t, rhs_t>)
    constexpr auto operator*(lhs_t&& lhs, rhs_t&& rhs) noexcept -> mul_expr<lhs_t, rhs_t>
    {
        return { std::forward<lhs_t>(lhs), std::forward<rhs_t>(rhs) };
    }
    
    // Results in general matrix multiplication
    template<typename lhs_t, typename rhs_t> requires is_gemm_supported_v<lhs_t, rhs_t>
    constexpr auto operator*(lhs_t&& lhs, rhs_t&& rhs) noexcept -> gemm_expr<lhs_t, rhs_t>
    {
        return { std::forward<lhs_t>(lhs), std::forward<rhs_t>(rhs) };
    }
    
  • Gemm表达式可以显式转换为hadamard表达式

    // explicitly create a hadamard multiplication expression
    auto c = static_cast<mul_expr>(a * b);
    
  • 有了这些规则,就可以添加api级别的抽象以保持清晰:

        // c = hadamard(a * b);
        template<expr_type expr_t> requires std::is_convertible_v<std::decay_t<expr_t>, mul_expr>
        constexpr auto hadamard(expr_t&& expr) noexcept
        {
            return static_cast<mul_expr>(expr);
        }
    
        // support c = hadamard_mult(a, b); syntax
        template<object_type lhs_t, object_type rhs_t> requires is_same_dimensions_v<lhs_t, rhs_t>
        constexpr auto hadamard_mult(lhs_t&& lhs, rhs_t&& rhs) noexcept
        {
            return hadamard(lhs * rhs);
        }
    
    可以(也应该)导致阿达玛乘法。此外,对类型系统的简单操作很容易导致函数语法容易支持的情况


    非常感谢你们在上面的评论中帮助我集思广益并得出这个结论。通过这里的讨论,我对我的库非常满意。

    *
    是通过访问成员的方法之一。您只能修改现有运算符或使用函数<代码>阿达玛(mat1,mat2)将是一个很好的样式。您不能在C++中创建新的操作符。您可以重载<代码>操作符> *>代码>,或者您可以超载<代码>操作程序()/>代码以返回一些包装对象,它本身重载<代码>操作符*(Mat const &)(允许<代码> Mat1> > * Mat2 < /COD>或<代码> Mat1(*):虽然我会说,通常说得冗长并不坏。例如,允许使用以下语法:
    mat1.array()*mat2.array()
    mat1.cwiseProduct(mat2)
    “这些语法似乎都没有Matlab的语法那么优雅”,但Hadamard(mat1*mat2)看起来更具可读性。
    *
    是通过,或者使用一个函数<代码>阿达玛(mat1,mat2)是一种好的样式。不能在C++中创建新的运算符。
    // explicitly create a hadamard multiplication expression
    auto c = static_cast<mul_expr>(a * b);
    
    // explicitly create an array using hadamard multiplication
    auto c = static_cast<array>(a * b);
    
     // if a is an array, and b is a matrix, then c is a mult_expr
     // if a is a matrix, and b is an array, then c is a gemm_expr
     auto c = a * b;
    
        // c = hadamard(a * b);
        template<expr_type expr_t> requires std::is_convertible_v<std::decay_t<expr_t>, mul_expr>
        constexpr auto hadamard(expr_t&& expr) noexcept
        {
            return static_cast<mul_expr>(expr);
        }
    
        // support c = hadamard_mult(a, b); syntax
        template<object_type lhs_t, object_type rhs_t> requires is_same_dimensions_v<lhs_t, rhs_t>
        constexpr auto hadamard_mult(lhs_t&& lhs, rhs_t&& rhs) noexcept
        {
            return hadamard(lhs * rhs);
        }
    
        c = a * b;