背景

我有一个在内部使用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已经部分支持它们。

    10-08 09:26