【问题标题】:C++ comma operator overloading and vector of referencesC++ 逗号运算符重载和引用向量
【发布时间】:2012-05-04 22:02:39
【问题描述】:

有很多关于 C++ 中逗号运算符重载的帖子(问题)。 大多数答案建议不要使用逗号运算符重载。 我想编写一个语法与 Matlab 语言非常相似的 c++ 库。 基本对象是 Matrix MX。我希望能够让库的最终用户编写如下表达式:

MX a = b(i);// get b elements at indices i 
b(i,j)= a; // set elements of b at indices i,j.

我知道如何使用保存指向 MX 对象的指针并保存索引 i,j 对象的代理类来使 setter 和 getter 像上面所写的那样工作。例如 b(i,j) 将创建一个代理对象 ProxMX(b,i,j)。然后我们定义一个方法将 ProxMX 分配给 MX 和 visversa(使用运算符 =),它们完成了获取和设置 b 元素的艰巨工作。

我需要帮助来进行函数调用,例如:

(x,y,z)= ff(a,b,c) 

其中 a、b、c 是输入参数(MX 对象),x、y、z 是输出参数。 如果上述语法是不可能的,我可以考虑这样的语法:

ff((a,b,c), (x,y,z) )

我开始编写这个测试代码:

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;




class MX {// Matrix 

public:
    MX(double va) {
        elem=va;// only one double for the moment to test the syntaxe
    }
    MX &operator ()(MX idx){ // get & set MX(i)
        return *this;//
    };
    MX &operator ()(MX idx1,MX idx2) { // get set MX(i,j)
        return *this;
    } ;
    friend ostream &operator<<(ostream &stream, MX a);

    double elem;

};

ostream &operator<<(ostream &stream, MX a)
{
  stream << a.elem ;

  return stream;
}

typedef vector<const MX > MXR;
class ArgList { // Proxy
public:
    //ArgList(const MX& a){
    //  data.push_back(a);
    //}
    ArgList() {};

    ArgList& operator , (const MX &a){
        data.push_back(a);
        return *this;
   }
   ArgList& operator =(ArgList& ins){
        for (int i=0 ;i <ins.data.size();i++)
            (this->data[i]).elem=ins.data[i].elem;
        return *this;
   };
    MXR data; 
};


ArgList operator , (const MX& a, const MX& b){
    ArgList out;    
    out.data.push_back(a);
    out.data.push_back(b);
    return out;
   }

ArgList ff(ArgList argins)
{

    int n = argins.data.size();
    MX a= argins.data[0];
    MX b= argins.data[1];
    MX x(a.elem+1.0);
    MX y(b.elem+10.0);
    MX z(a.elem+b.elem);
    return ( x, y , z);

}
void gg(ArgList argins, ArgList &argout)
{

    int n = argins.data.size();
    MX a= argins.data[0];
    MX b= argins.data[1];
    MX x(a.elem+1.0);
    MX y(b.elem+10.0);
    MX z(a.elem+b.elem);
    argout = ( x, y , z);

}
int _tmain(int argc, _TCHAR* argv[])
{
    MX a(1.0);MX b(2.0);MX c(3.0);
    MX aa = a(MX(3.0));
    aa(MX(2.0),MX(3.0))=MX(5.0);
    cout << "a=" << a << ",b=" << b << ",c=" << c << endl;
    MX x(0.0);MX y(0.0);MX z(0.0);
    cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
    (x,y,z)= ff((a , b, c ));
    cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
    gg((a,b,c) , (x,y,z));
    cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
    return 0;
}

此代码使用 VS2010 Express 编译和运行没有错误:)。但正如预期的那样,它没有给出预期的结果,因为我需要在 ArgList 中保存对变量的引用,而不是将对象复制到向量中。我知道我们不能使用 std::vector 作为对象引用的容器。

为了使这些表达式可写并在 c++ 代码中工作的任何帮助。 谢谢。

【问题讨论】:

  • 您能否详细说明您的实际问题?它的行为不符合您的预期?除了std::vector,您还需要其他容器吗?还有什么?
  • 我需要帮助才能使 (x,y,z)=ff((a,b,c)) 等表达式在 C++ 中调用函数。我的第一个测试代码让我认为接受对 ojects 的引用的容器可能是正确的方向。但我不是sur。
  • 现有许多 C++ 开源线性代数库旨在提供类似 MATLAB 的语法,例如 ArmadilloBlitz++。你可以看看他们的来源。
  • 我已经看过 Blitz++,我认为在 c++ 中制作索引运算符“a la Matlab”是可能的。但我不认为 c++ 中的函数调用 à la Matlab 已经完成。

标签: c++ reference operator-overloading comma


【解决方案1】:

我正在使用 C++11 为您概述一个解决方案(代码也经过测试),但这在 C++03 中是可行的(使用例如 Boost.Tuple):

// Base case
template<typename Lhs, typename Rhs>
std::tuple<Lhs&, Rhs&>
operator,(Lhs& lhs, Rhs& rhs)
{
    return std::tie(lhs, rhs);
}

// General case when we already have a tuple
template<typename... Lhs, typename Rhs>
std::tuple<Lhs..., Rhs&>
operator,(std::tuple<Lhs...>&& lhs, Rhs& rhs)
{
    return std::tuple_cat(lhs, std::tie(rhs));
}

用法如下(假设此运算符位于namespace ns):

// Declaration: note how ff must return a tuple
std::tuple<X, Y, Z> ff(A, B, C);

A a = /* ... */;
B b = /* ... */;
C c = /* ... */;
X x; Y y; Z z;

using ns::operator,;
// brackets on the left-hand side are required
(x, y, z) = ff(a, b, c);

以下是附加的警告:

  • 如您所见,您需要一个 using 声明才能将 operator, 纳入范围。即使XYZ 类型位于operator, 的相同范围内(启用 ADL),std::tuple 也不会。您可以执行类似template&lt;typename... T&gt; struct tuple: std::tuple&lt;T...&gt; { using std::tuple&lt;T...&gt;::tuple; }; 之类的操作(但是在 C++03 中不那么方便),以便在适当的命名空间中拥有自己的元组,以便以快速而简单的方式使用 ADL。然而:

  • 重载运算符必须始终对至少一种用户定义类型进行操作。因此,如果XY 类型恰好都是intdouble 之类的类型,那么您将获得默认的operator,。此类事情的通常解决方案是要求客户端执行类似(ref(x), ref(y), z) = ff(a, b, c); 的操作,其中ref 将返回适当命名空间中的类型(再次用于ADL)。也许这种类型可以用std::reference_wrapper(或 C++03 的 Boost 版本)的方式实现,使用与元组相同的快速和肮脏的 hack。 (您需要额外的 operator, 重载。)

总而言之,当类似

/* declaration of ff and variable definitions the same as before */
std::tie(x, y, z) = ff(a, b, c);

或许

/* this skips unnecessary default constructions of x, y, z */
auto tuple = ff(a, b, c);
using std::get;
auto& x = get<0>(tuple);
auto& y = get<1>(tuple);
auto& z = get<2>(tuple);

开箱即用(即使在带有 Boost.Tuple 的 C++03 中)。我在这件事上的建议是(无意):保持简单,愚蠢!并使用它。

【讨论】:

  • 非常感谢。我现在无法尝试此代码,因为我使用的是 Visual Studio 2010。C++11 目前不是一个真正的选择,因为它可能在不久的将来。顺便说一句,ns::operator 是什么,?
  • 对于非 c++ 程序员(比如我的 lib 的最终用户)如果我不能在 c++ 中编写 (x,y)=ff(a,b) 我认为编写 ff(a, b,IO_DUMMY_SEP,x,y) 比 auto tuple & get 解决方案更优雅。你同意吗?
  • @Yazou 同样,这一切都可以使用 C++03(std::tuple 主要基于 Boost.Tuple 的工作),尽管有些事情实现起来可能有些痛苦(例如,由于缺少可变参数模板)。 ns::operator, 在名为ns 的虚构命名空间中指定operator,;因为您不会重载全局命名空间中的运算符,更不用说包罗万象的operator,。最后记住,我推荐的first选项是让用户写X x; Y y; tie(x, y) = ff(a, b);;第二种变体是一种特殊用途(用于默认构造不可能或成本太高的情况)。
  • 再次感谢。我没有引起注意,您已经在 cmets 中解释了 ns“命名空间”。我将深入研究 C++03 中的 std::tuple ,看看是否可以制作 (x,y) =ff(a,b). 的工作示例
  • @Yazou 在 C++03 中你可能会使用 Boost.Tuple,意思是 boost::tuple。 C++03 中没有std::tuple,尽管您的编译器可能支持 TR1,它确实有一个元组。在任何情况下,我都不建议重新发明没有可变参数模板的元组(仅限 C++11 的功能)。
【解决方案2】:

在 C++11 中,您可以使用元组来做到这一点:

std::tuple<some_type, another_type, yet_another_type> ff(...);

some_type x;
another_type y;
yet_another_type z;

std::tie(x,y,z) = ff(a,b,c);

如果您的编译器不支持,Boost.Tuple 库非常相似,但在没有可变参数模板支持的情况下可能会受到更多限制。

如果你真的想支持像(x,y,z) = ff(a,b,c); 这样的语法,即使它在Matlab 中看起来很合理,这可能会让C++ 程序员感到困惑,那么你就差不多了。您需要一个类似于ArgList 的单独类型(可能称为RefList 之类的东西),它包含指针而不是值,从对结果的非const 引用初始化这些指针,并具有一个赋值运算符,该运算符采用ArgList(或其他一些值集合)并通过指针分配每个元素。

您可能还想查看Boost.Assignment 以了解如何使这种运算符重载起作用。这有点麻烦;特别是,您不能重载仅作用于内置类型的运算符,这反而会限制该方法的实用性。就个人而言,如果 C++11 是一个选项,我会使用可变参数模板。

【讨论】:

  • 非常感谢。我已经考虑过 Boost.Assignement 但我不知道 C++11 中的 std::tuple 。我想要的是避免为我的 lib 的最终用户使用 std::anything(x,y,z,w,t) 。我可以在我的库内部编写代理类,以使最终用户的语法非常简单和干净。我的最终用户不是 C++ 程序员,而是 Matlab 程序员。如果 std::tuple 有效,我必须提供逗号运算符,该运算符将在场景中构建元组,以便生成最终表达式: (x,y,z,t,w)=ff(a,b,c,d,f,g )。
  • @Yazou:我已经添加了一些关于如何实现这一目标的指示。请注意,这会使习惯于 C++ 的人看起来很奇怪。
  • @Mile:再次感谢。如果 C++11 不是一个选项。我可以在我的第一个尝试代码(ArgList 代理)中使用哪些候选容器来保存对 MX 对象的引用?
  • @Yazou: std::vector&lt;MX*&gt; 是最简单的。
  • 太棒了 :) 我正在尝试这个解决方案,但我遇到了一些关于 assiging non const MX * 的错误。
【解决方案3】:

感谢大家的建议和反馈。 这是一个使用 2 个代理的工作示例。 ArgList 包含指向 MX 对象的指针,而 ArgCopyList 包含 MX 对象的副本。

从 matlab 用户的角度来看,c++ 语法 (x,y,...)=myfunc((a,b,...)) 与 matlab 表达式 [x,y,...]=myfunc(a,b,...) 非常相似。但我的第一印象是这种函数调用根本没有效率,因为返回的值被复制了,这可以通过引用传递输出来避免。

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

class MX {// Matrix 

public:
    MX(double va) {
        elem=va;// only one double for the moment to test the syntaxe
    }
    MX & operator = (const MX &src)
    {   elem = src.elem;
        return *this;
    }
    friend ostream &operator<<(ostream &stream, MX &a);

    double elem;

};

ostream &operator<<(ostream &stream, MX &a)
{
  stream << a.elem ;

  return stream;
}

typedef vector<MX *> MXR; // save pointers only 

class ArgCopyList { // copy the objects to a list
public :
    vector<const MX> data; 
    ArgCopyList(MX &a)
    {
        data.push_back(a);
    };
    ArgCopyList(MX &a, MX &b)
    {
        data.push_back(a);
        data.push_back(b);
    };
    ArgCopyList(MX &a, MX &b, MX &c)
    {
        data.push_back(a);
        data.push_back(b);
        data.push_back(c);
    };
    // do the same for bigger lists

};

class ArgList { // Proxy
public:

    ArgList() {};

    ArgList(const ArgList& src) 
    {
        data.clear();
        for (int i=0 ;i <src.data.size();i++)
            data.push_back(src.data[i]);
    }
    ArgList& operator , ( MX &a){
        data.push_back(&a);
        return *this;
   }

    ArgList  &operator= ( ArgList &src)
    {
        if (this == &src)
            return *this;
        data.clear();
        int n= src.data.size();
        for (int i=0 ;i <n;i++)
            data.push_back(src.data[i]);
        return *this;
    };


   ArgList& operator =( ArgCopyList& src){
        for (int i=0 ;i <data.size();i++)// TBD : must control the size of src & this->data here
            data.at(i)->elem = (src.data[i].elem);
        return *this;
   };
    MXR data; 
};


ArgList operator , (MX& a,  MX& b){
    ArgList out;    
    out.data.push_back(&a);
    out.data.push_back(&b);
    return out;
   }

// test function
ArgCopyList ff(ArgList argins)
{

    int n = argins.data.size();
    MX a= *(argins.data[0]);
    MX b= *(argins.data[1]);
    MX x(a.elem+1.0);
    MX y(b.elem+10.0);
    MX z(a.elem+b.elem);
    return ArgCopyList( x, y , z);

}


int _tmain(int argc, _TCHAR* argv[])
{

    MX a(1.0);MX b(2.0);MX c(3.0);
    cout << "a=" << a << ",b=" << b << ",c=" << c << endl;

    MX x(0.0);MX y(0.0);MX z(0.0);
    cout << "Argouts before calling (x,y,z)= ff((a,b,c)).\nx=" << x << ",y=" << y << ",z=" << z << endl;

    (x,y,z)= ff((a , b, c) );

    cout << "Argouts after calling ff.\nx=" << x << ",y=" << y << ",z=" << z << endl;
    return 0;
}

/* output
a=1,b=2,c=3
Argouts before calling (x,y,z)= ff((a,b,c)).
x=0,y=0,z=0
Argouts after calling ff.
x=2,y=12,z=3
*/

【讨论】:

    猜你喜欢
    • 2016-08-22
    • 1970-01-01
    • 2013-11-10
    • 2011-08-01
    • 2013-05-15
    • 2020-03-09
    • 2011-01-24
    • 2017-07-11
    • 2010-12-16
    相关资源
    最近更新 更多