背景
我有一个在内部使用vector 的容器类。我已经为此包装类提供了一种方法AddChar(std::string),该类对内部 vector 执行push_back()。在我的代码中,我必须在一段时间内将多个项目添加到容器中。为此,我必须使用
container.AddChar("First");
container.AddChar("Second");
这使代码更大。因此,为了使其更容易,我计划重载运算符<
container << "First" << "Second"
并且两个项目将被添加到基础 vector 中。
这是我用于此的代码
class ExtendedVector
{
private:
vector<string> container;
public:
friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){
cont.AddChar(str);
return cont;
}
void AddChar(const std::string str)
{
container.push_back(str);
}
string ToString()
{
string output;
vector<string>::iterator it = container.begin();
while(it != container.end())
{
output += *it;
++it;
}
return output;
}
};
它正在按预期方式工作。
问题
有什么想法吗?
编辑
听完精彩的评论后,我决定不重载<
class ExtendedVector
{
private:
vector<string> container;
public:
ExtendedVector& AddChar(const std::string str)
{
container.push_back(str);
return *this;
}
.. other methods
}
这使我可以添加
container.AddChar("First").AddChar("Second")
在C#中,我可以使用params关键字更轻松地做到这一点。代码会像
void AddChar(params string[] str)
{
foreach(string s in str)
// add to the underlying collection
}
我知道在C++中,我们可以使用...来指定参数的可变长度。但是AFAIK,它不是类型安全的。那么这样做是推荐的做法吗?这样我就可以写
container.AddChar("First","Second")
感谢您的答复。
最佳答案
是的,但是可以做得更好。就像提到的其他人一样,可以完全从现有的公共(public)功能中定义您的功能。为什么不让它们仅使用那些?现在,它是一个 friend ,这意味着它属于实现细节。如果将operator <
class ExtendedVector {
...
};
// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
cont.AddChar(str);
return cont;
}
如果您更改类(class),您将不确定您的运算符(operator)<
正如另一个人再次说的那样,这是有争议的。在许多情况下,运算符(operator)重载乍看之下看起来“很整洁”,但明年看起来会很糟糕,因为您再也不知道给某些符号加特殊符号时要记住的想法了。在operator <
QStringList items;
items << "item1" << "item2";
类似的情况是
boost.format
,它也重用operator%
在其字符串中为占位符传递参数:format("hello %1%, i'm %2% y'old") % "benny" % 21
当然在那儿使用它也是有争议的。但是它对printf格式指定的用法是众所周知的,因此,恕我直言,在这里也可以使用。但是像往常一样,风格也是主观的,所以要加一点盐:)
好吧,如果您要寻找同类参数,则可以采用一种接受 vector 的方式:
void AddChars(std::vector<std::string> const& v) {
std::vector<std::string>::const_iterator cit =
v.begin();
for(;cit != v.begin(); ++cit) {
AddChar(*cit);
}
}
尽管通过它并不是很舒服。您必须手动构造 vector ,然后再传递...我看到您对vararg样式函数已经有了正确的认识。请勿将它们用于此类代码,并且仅在与C代码接口(interface)或调试功能时使用。处理这种情况的另一种方法是应用预处理程序。这是一个高级主题,而且很hacky。这个想法是自动生成重载,上限大约是这样:
#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
/* now access arg0 ... arg(X-1) */ \
/* AddChar(arg0); ... AddChar(arg(N-1)); */ \
GEN_PRINT_ARG1(X, AddChar, arg) \
}
/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)
那是伪代码。您可以看一下boost预处理程序库here。
下一代C++版本将提供更好的可能性。可以使用初始化程序列表:
void AddChars(initializer_list<std::string> ilist) {
// range based for loop
for(std::string const& s : ilist) {
AddChar(s);
}
}
...
AddChars({"hello", "you", "this is fun"});
在下一个C++中,还可以使用variadic templates支持任意多个(混合类型)参数。 GCC4.4将支持它们。 GCC 4.3已经部分支持它们。