这种“,”运算符的使用是否被视为不良形式?

时间:2011-07-18 05:04:22

标签: c++ operator-overloading comma

我已经创建了一个列表类,用于替换我的程序中的可变参数函数,用于初始化需要包含更改的元素列表的对象。 list类有一个我非常喜欢的用法语法。但是我之前没有看过它,所以我想知道我是不是因为这个事实而不应该使用它?列表类的基本实现看起来像这样......

#include <list>
#include <iostream>
template<typename T>
struct list
{
    std::list<T> items;
    list(const list&ref):items(ref.items){}
    list(){}
    list(T var){items.push_back(var);}
    list& operator,(list add_){
        items.insert(items.end(),add_.items.begin(), add_.items.end());
        return *this;
    }
    list& operator=(list add_){
        items.clear();
        items.insert(items.end(),add_.items.begin(), add_.items.end());
        return *this;
    }
    list& operator+=(list add_){
        items.insert(items.end(),add_.items.begin(), add_.items.end());
        return *this;
    }
};

这允许我在代码中使用它...

struct music{
//...
};
struct music_playlist{
    list<music> queue;
//...
};
int main (int argc, const char * argv[])
{
    music_playlist playlist;
    music song1;
    music song2;
    music song3;
    music song4;
    playlist.queue = song1,song2; // The queue now contains song1 and song2
    playlist.queue+= song1,song3,song4; //The queue now contains two song1s and song2-4
    playlist.queue = song2; //the queue now only contains song2
    return 0;
}

我真的认为语法比我刚刚暴露了常规stl容器,甚至比可变参数函数更好(和类型安全)时的语法更好。但是,由于我没有看到使用这种语法,我很好奇是否应该避免使用它,因为其他程序员应该很容易理解代码?

修改

与此问题相关,我发布了question更多针对实际问题的解决方案。

8 个答案:

答案 0 :(得分:40)

为什么不像QList那样重载<<运算符?然后使用它:

playlist.queue << song1 << song2; // The queue now contains song1 and song2
playlist.queue << song1 << song3 << song4; //The queue now contains two song1s and song2-4

答案 1 :(得分:20)

我同意你写的语法很好看 我对代码的主要困难是我希望以下内容是相同的

playlist.queue = song1,song2;
playlist.queue = (song1,song2);  //more of c-style, as @Iuser notes.
实际上它们完全不同。

这很危险,因为它很容易在代码中引入使用错误。 如果有人喜欢使用括号来为分组添加额外的重点(并非罕见),那么逗号可能会成为一个真正的痛苦。例如,

//lets combine differnt playlists
new_playlist.queue =    song1        //the first playlist
                      ,(song3,song4) //the second playlist //opps, I didn't add song 3!
                      , song5;        //the third 

new_playlist.queue = (old_playlist.queue, song6); //opps, I edited my old playlist too!

很明显,你遇到过boost.assign:http://www.boost.org/doc/libs/1_47_0/libs/assign/doc/index.html

答案 2 :(得分:11)

最近是否改变了优先顺序?

playlist.queue = song1,song2;

这应解析为:

(playlist.queue = song1) , song2;

你的','和'+ ='是一样的! 如果您的逗号运算符要创建临时列表,插入左侧和右侧项并返回临时项,那将是更好的语义匹配。然后你就可以这样写;

playlist.queue = (song1,song2);

明确的parens。这将使C程序员有机会阅读代码。

答案 3 :(得分:7)

有一个问题是,如果编译器无法选择重载的运算符逗号,它可以使用内置运算符重新使用。

相反,使用Boost.Assign混合类型会产生编译错误。

#include <boost/assign.hpp>

int main()
{
    int one = 1;
    const char* two = "2";
    list<int> li;
    li = one, two;

    using namespace boost::assign;
    std::list<int> li2;
    li2 += one, two;
}

答案 4 :(得分:5)

这可能属于程序员,但这是我的两分钱。

如果您正在谈论具有相当狭窄的上下文的代码,用户将在几个地方使用它,那就是全部,那么重载,运算符可能就行了。如果您正在构建特定域中使用的特定于域的语言,那么它可能没问题。

当您为某些预期用户以某种频率使用的内容超载时会出现问题。

重载,意味着读者需要完全重新解释他们如何阅读您的代码。他们不仅可以查看表达式,还可以立即知道它的作用。你正在搞乱C ++程序员在扫描代码时所做的一些最基本的假设。

这样做是你自己的危险。

答案 5 :(得分:5)

  

我很好奇我是否应该避免它,因为最重要的是   其他程序员应该很容易理解代码

如果目标是让您的代码易于让其他C ++程序员理解,那么重写操作符以赋予它们与标准C ++非常不同的含义并不是一个好的开始。读者不应该a)了解你如何实现容器,b)重新校准他们对标准操作符的理解,以便能够理解你的代码。

我很欣赏Boost precedent这类事情。如果您非常确定大多数将阅读您的代码的人也熟悉Boost Assign,那么您自己的操作符覆盖可能非常合理。不过,我建议遵循@ badzeppelin建议使用operator&lt;&lt;相反,就像iostreams一样。每个C ++开发人员都可以依赖于以下代码:

cout << "Hello world!"`

并且你的追加操作与写入流非常相似。

答案 6 :(得分:4)

在很多层面都很糟糕......

您将覆盖list并隐藏std::list。一个很大的禁忌。如果您想要自己的列表类 - 使用不同的名称,请不要隐藏标准库。

以这种方式使用,是不可读的。运算符的返回值是右操作数。即使您的代码有效,但对于外部阅读器来说,这并不是很明显的原因,这也是一件坏事。代码应该是可读的,不是很好。

答案 7 :(得分:2)

使用逗号operator ,具体没什么用。如果被剥削,任何操作员都会留下不好的味道在您的代码中,我没有看到任何合理的问题。我只想提出一个建议:

list& operator,(list &add_){  // <--- pass by reference to avoid copies
  *this += add_;  // <--- reuse operator +=
  return *this;
}

这样,如果您想要更改逻辑,则必须始终只编辑operator +=。请注意,我的答案是从可读性和代码维护的角度来看一般。我不会对您使用的业务逻辑表示担忧。