28

我制作了一个列表类,作为替换程序中可变参数函数的一种方法,用于初始化需要包含不断变化的元素列表的对象。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 容器要好得多,甚至比可变参数函数更好(和类型安全)。但是,由于我没有看到使用过这种语法,我很好奇是否应该避免使用它,因为首先代码应该很容易被其他程序员理解?

编辑:

结合这个问题,我发布了这个问题,更针对实际问题的解决方案。

4

8 回答 8

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
于 2011-07-18T05:19:25.390 回答
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

于 2011-07-18T06:22:00.797 回答
11

最近优先级有变化吗?

playlist.queue = song1,song2;

这应该解析为:

(playlist.queue = song1) , song2;

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

playlist.queue = (song1,song2);

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

于 2011-07-18T05:37:13.433 回答
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;
}
于 2011-07-18T06:50:57.550 回答
5

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

如果您正在谈论具有相当狭窄上下文的代码,用户将在几个地方使用它,仅此而已,那么重载,运算符可能是可以的。如果您正在构建一种特定领域的语言,该语言用于特定领域而不是其他任何地方,那可能没问题。

当您为希望用户以某种频率使用的东西重载它时,问题就出现了。

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

这样做后果自负。

于 2011-07-18T05:27:14.977 回答
5

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

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

我可以欣赏这类事情的Boost 先例。如果您非常确定大多数阅读您的代码的人也会熟悉 Boost Assign,您自己的运算符覆盖可能是相当合理的。尽管如此,我还是建议按照@badzeppelin 的建议使用 operator<<,就像 iostreams 一样。可以指望每个 C++ 开发人员都遇到过以下代码:

cout << "Hello world!"`

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

于 2011-07-18T07:53:30.837 回答
4

在很多层面上都是糟糕的......

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

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

于 2011-07-18T05:13:12.197 回答
2

专门使用逗号并没有什么不好operator ,。如果被利用,任何运营商都会留下不良品味。在您的代码中,我没有看到任何合理的问题。只有一个建议,我想给出的是:

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

这样operator +=,如果您想对逻辑进行任何更改,您必须始终编辑 just 。请注意,我的回答通常是从可读性和代码维护的角度来看。我不会对您使用的业务逻辑提出担忧。

于 2011-07-18T05:13:45.453 回答