如何在C ++中将惰性求值与auto结合使用

时间:2017-09-20 12:07:40

标签: c++ c++11 templates

我尝试做的一些背景: 我正在尝试实现一个做量子力学的库。由于量子力学基本上只是线性代数,我使用下面的犰狳线性代数库。 Armadillo使用惰性求值来对矩阵做一些聪明的技巧,这样可以很好地抽象出实际情况并且看起来接近matlab代码。

我想做类似的事情,但我也希望能够使用auto,这是犰狳(或本征)无法实现的。

我一直在四处寻找,这个答案包含我认为实现这一点的典型方式:https://stackoverflow.com/a/414260/6306265

这种方法的问题在于你写的时候

auto C = A+B;

你得到的Cmatrix_add,而不是matrix。即使matrix_add的行为与matrix的行为相似,matrix_add包含对AB的引用的事实也会使其难以随身携带。例如。

auto A = matrix(2,2,{0,1,0,1});
auto B = matrix(2,2,{1,0,1,0});
auto C = A+B;
C.printmatrix(); // 1,1 ; 1,1

auto A = matrix(2,2,{0,1,0,1});
auto B = matrix(2,2,{1,0,1,0});
auto C = A+B;
A(0,0) = 1;
C.printmatrix(); // 2,1 ; 1,1

这是违反直觉的。由于数学上直观的行为是我想要实现的,这是一个问题。

更糟糕的是我什么时候

auto sumMatrices(const matrix& A, const matrix& B)
{
    return A+B;
}

返回matrix_add,引用本地内存。

我真的希望能够拥有漂亮的重载行为,但也能够使用auto。我的想法是创建一个可以包含引用或实例的包装器:

template<class T>
class maybe_reference
{
public:
    maybe_reference(const T& t):
        ptr_(std::make_unique<T>(t)),
        t_(*ptr_)
    {}
    maybe_reference(std::reference_wrapper<const T> t): 
        t_(t.get())
    {}

    const T& get(){return t_;}
private:
    unique_ptr<T> ptr_;
    const T& t_;
}

它可能无法以这种方式实现,但一般的想法是有两个可以清楚区分的构造函数,以确保get()返回引用的对象或unique_ptr中的对象。

修改后的matrix_add

class matrix_add {
public:
    friend matrix_add operator+(const matrix& A, const matrix& B);

    matrix_add(matrix_add&& other): A_(other.A_.get()), B_(other.B_.get()){}
private:
    matrix_add(const matrix& A, const matrix& B): A_(std::ref(A)), B_(std::ref(B)){}

    maybe_reference<matrix> A_;
    maybe_reference<matrix> B_;
};

我遗漏了使matrix_add表现得像matrix的所有部分。想法是让对象引用外部对象A&amp; B,只要它是用A + B构造的,但是当它被移动构造时,它将拥有副本。

我的问题基本上是:这有用吗?

我一直认为移动构造函数可能会在某些或所有情况下被删除,这可能是毁灭性的。

此外,还有其他方法可以实现同样的目标吗?我一直在寻找,但似乎线性代数至少是懒惰或自动。

编辑:由于被提醒了术语&#34;表达模板&#34;,我的谷歌搜索更有成效。我找到了这个reddit-post:https://www.reddit.com/r/cpp/comments/4puabu/news_about_operator_auto/
和引用的论文,允许规范&#34;演员&#34;到汽车。这将是真正使所有这些工作的功能。

4 个答案:

答案 0 :(得分:1)

您可以编写模板函数dist/bundle.js,默认情况下是NOP,然后根据需要重载。

after.js

答案 1 :(得分:1)

我将定义一个新的运算符:eager_eval,如下所示:

namespace lazy {
  template<class T>
  void eager_eval(T const volatile&)=delete;

  template<class T>
  struct expression {
    template<class D,
      std::enable_if_t<std::is_base_of<expression, std::decay_t<D>>{}, int> =0
    >
    friend T eager_eval( D&& d ) { return std::forward<D>(d); }
  };
}

每当您希望以急切的方式评估某些内容时,请在其名称空间中定义eager_evallazy::lazy_expression<target_type>派生。

因此,我们将您的matrix_add修改为(A),并使用您想要的延迟生成类型进行修改,然后(B)使用运算符matrix

struct matrix_add:
  lazy::expression<matrix>
{
  matrix_add(matrix const& a, matrix const& b) : a(a), b(b) { }

  operator matrix() && { // rvalue ref qualified as it should be.
    matrix result;
    // Do the addition.
    return result;
  }
private:
  matrix const& a, b;
};

现在,任何人都可以这样做:

auto e = eager_eval( a+b );

并且ADL找到正确的类型以急切评估延迟表达式。

live example

您可以选择实现返回其参数的默认eager_eval

  template<class T, class...Ts>
  T eager_eval(T&& t, Ts&&...)  { return std::forward<T>(t); }

然后

using lazy::eager_eval;
auto x = eager_eval( 1+2 );

让您与传递给eager_eval的类型无关;如果它是一个通过eager_eval重载知道延迟的类型,它会转换,如果不是,它就不会转换。

上面lazy::eager_eval中的包是为了确保它具有最低优先级作为过载。

答案 2 :(得分:1)

我认为,你的基本问题是,懒惰的评估与改变状态不能很好地融合。我看到了两条可能的路线:

  1. 让您的矩阵不可变。如果您“修改”矩阵,您实际上创建了包含更改的副本,原始文本保持不变。这在语义上很有效(任何数学运算都与您期望的完全一样),但如果您按值设置矩阵值,则可能会产生无法忍受的运行时开销。

    这允许matrix_add的实现在评估时以matrix对象静默替换自身,确保每次评估最多只执行一次。

  2. 使您的函数显式化。不要创建matrix_add个对象,这些对象本身就像它们是矩阵一样,但是创建matrix_function个对象可以对某个输入进行操作矩阵产生一些结果。这允许您在您认为合适的位置显式执行评估,并重用您定义的功能。但是,这种方法会导致许多额外的代码复杂性。

  3. 我认为通过引入强制评估的隐含点来尝试解决这个问题并不是一个好主意:你将放弃懒惰评估可以实现的大部分内容,那么为什么首先要烦恼呢?只是我的两分钱。

答案 3 :(得分:0)

用c ++ 17类模板参数推导,你可以写

class Program
{
    static void Main(string[] args)
    {
        List<course> Courses = new List<course>();
        Courses.Add(new course() { name = "CA", courseID = 1 });
        Courses.Add(new course() { name = "CB", courseID = 2 });
        Courses.Add(new course() { name = "CC", courseID = 3 });

        string column_name = "name";
        string column_value = "C";
        string where = string.Format("{0}.Contains(@0)", column_name); //first create the where clause with the column name
        var result = Courses.Where(where,column_value).Select("new(name,courseID)").Take(50);   //here apply the column value to the name
        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
        Console.ReadLine();
    }
}

public class course
{
    public string name { get; set; }
    public int courseID { get; set; }
}

其中struct matrix_expr_foo {}; struct matrix_expr_bar {}; template< typename L, typename R > struct matrix_add { // ... }; matrix_add<matrix_expr_foo,matrix_expr_bar> operator + (matrix_expr_foo const& a, matrix_expr_bar const& b); template< typename T > struct expr { expr( T const& expr ){ // evaluate expr ( to be stored in an appropriate member ) } // ... }; int main() { auto a = matrix_expr_foo(); auto b = matrix_expr_bar(); expr c = a + b; /* different naming ? auto_ c = a + b; ... */ } 表示作为表达式模板的expr ...