我已经创建了一个列表类,用于替换我的程序中的可变参数函数,用于初始化需要包含更改的元素列表的对象。 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更多针对实际问题的解决方案。
答案 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 +=
。请注意,我的答案是从可读性和代码维护的角度来看一般。我不会对您使用的业务逻辑表示担忧。