这种对","运算符的使用是否被认为是错误的形式?

Is this use of the "," operator considered bad form?

本文关键字:认为是 错误 是否 运算符      更新时间:2023-10-16

我创建了一个列表类,作为替代程序中用于初始化需要包含不断变化的元素列表的对象的可变函数的方法。list类有一种我非常喜欢的用法语法。然而,我以前没有见过它被使用,所以我想知道我是否应该仅仅因为这个事实而使用它?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容器,那么语法会好得多,甚至比可变函数更好(并且类型安全)。然而,由于我没有看到这种语法的使用,我很好奇我是否应该避免它,因为所有的代码都应该很容易被其他程序员理解。

编辑:

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

为什么不像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

我同意你的语法看起来很好,因为你已经写了。
这段代码的主要困难在于,我希望下面的内容与

相同
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

最近优先级有变化吗?

playlist.queue = song1,song2;

解析为:

(playlist.queue = song1) , song2;

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

playlist.queue = (song1,song2);
带有显式父元素的

。这将给c语言程序员一个努力读懂代码的机会。

一个小问题是,如果编译器不能选择您的重载操作符逗号,它可以转而使用内置操作符。

相反,对于Boost。分配混合类型会产生编译错误。

#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;
}

这可能是属于程序员的事情,但这里有我的两点意见。

如果你谈论的代码具有相当狭窄的上下文中,用户将在几个地方使用它,那么重载,操作符可能是可以的。如果您正在构建一种特定于领域的语言,该语言仅在特定领域中使用,而不在其他地方使用,那么它可能很好。

当你为一些你期望用户经常使用的东西重载时,问题就来了。

重载,意味着读者需要完全重新解释他们如何阅读你的代码。他们不可能只看一个表情就立刻知道它是干什么的。当涉及到扫描代码时,你正在扰乱c++程序员所做的一些最基本的假设。

做那件事自担风险。

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

如果你的目标是让其他c++程序员更容易理解你的代码,覆盖操作符来赋予它们与标准c++非常不同的含义并不是一个好的开始。读者不应该为了理解你的代码而a)理解你是如何实现容器的,b)重新校准他们对标准操作符的理解。

我可以欣赏Boost在这方面的先例。如果您非常确定大多数阅读您代码的人也熟悉Boost Assign,您自己的操作符重写,可能是非常合理的。尽管如此,我还是建议遵循@badzeppelin的建议,使用operator<<相反,就像iostreams一样。每个c++开发人员都应该遇到过这样的代码:

cout << "Hello world!"`

和追加操作非常类似于写入流。

不好在很多层面上…

您正在覆盖list并遮蔽std::list。这是一个大禁忌。如果你想要你自己的列表类——让它有一个不同的名字,不要遮蔽标准库。

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

使用逗号operator ,并没有什么不好。如果被剥削,任何经营者都会留下不好的印象。在你的代码中,我没有看到任何合理的问题。我想给出的唯一建议是:

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

这样,如果你想改变逻辑,你必须总是只编辑operator +=。请注意,我的回答是从可读性和代码维护的角度来考虑的,一般来说。我不会关注您使用的业务逻辑。