【问题标题】:Why does std::transform utilize the constructor?为什么 std::transform 使用构造函数?
【发布时间】:2012-07-12 14:16:53
【问题描述】:

我使用 std::transform 将一些值添加到列表中的现有值。下面的代码工作正常,我只是想知道在执行转换时是否可以避免所有对复制构造函数的调用(参见程序的输出)。如果我只是修改代码,并创建一个 for 循环,显式调用 Base 的 += 运算符,则不会执行复制构造,并且会更改值,这样会更有效。

我可以使转换调用 Base 的 operator+= 而不是复制构造吗?我应该专注于increment<Type>吗?

程序:

#include <iostream>
#include<list>
#include <algorithm>
#include <iterator>

template<class T>
class Base;

template<class T>
std::ostream& operator << (std::ostream& os, const Base<T>& b);

template<class T>
class Base
{
    private: 
        T b_;
    public: 
        typedef T value_type;

        Base()
            :
                b_()
        { std::cout << "Base::def ctor" << std::endl; }

        Base (const T& b)
            : 
                b_(b)
        { std::cout << "Base::implicit conversion ctor: " << b_ << std::endl; }

        const T& value()
        {
            return b_;
        }

        const Base operator+(const Base& b) const
        {
            std::cout << "Base operator+ " << std::endl;
            return Base(b_ + b.b_);
        }

        const Base&  operator+=(const T& t) 
        {
            b_ += t;
            return *this;
        }

        friend std::ostream& operator<< <T> (std::ostream& os, const Base<T>& b);
};

template<class T>
std::ostream& operator<< (std::ostream& os, const Base<T>& b)
{
    os << b.b_; 
    return os;
}

template<class Type> 
class increment
{
    typedef typename Type::value_type T; 

    T initial_; 

    public: 

        increment()
            :
                initial_()
        {};

        increment(const T& t)
            :
                initial_(t)
        {}

        T operator()()
        {
            return initial_++;
        }
};

template<class Container>
void write(const Container& c)
{
    std::cout << "WRITE: " << std::endl;
    copy(c.begin(), c.end(), 
         std::ostream_iterator<typename Container::value_type > (std::cout, " "));
    std::cout << std::endl;
    std::cout << "END WRITE" << std::endl;
}

using namespace std;

int main(int argc, const char *argv[])
{
    typedef list<Base<int> > bList; 

    bList baseList(10); 

    cout << "GENERATE" << endl;
    generate_n (baseList.begin(), 10, increment<Base<int> >(10));
    cout << "END GENERATE" << endl;

    write(baseList); 

    // Let's add some integers to Base<int>

    cout << "TRANSFORM: " << endl;

    std::transform(baseList.begin(), baseList.end(), 
                   baseList.begin(), 
                   bind2nd(std::plus<Base<int> >(), 4)); 
    cout << "END TRANSFORM " << endl;

    write(baseList);

    // Hacking the code: 
    cout << "CODE HACKING: " << endl;
    int counter = 4;
    for (bList::iterator it = baseList.begin(); 
         it != baseList.end(); 
         ++it)
    {
        *it += counter; // Force the call of the operator+=
        ++counter;
    }
    write (baseList);
    cout << "END CODE HACKING" << endl;

    return 0;
}

【问题讨论】:

  • std::for_each 带有一个谓词增量会更好,这里是 IMO。
  • 感谢您的提示,如果您将其写成答案,我会接受。
  • 我会留下评论,因为我认为它不能完全回答你的问题:int counter = 4; std::for_each(baseList.begin(), baseList.end(), [&amp;counter](int&amp; i){ i += counter++; }); 如果你想留在 C++03 中,我可能会编写一个自定义函子和称它为plus_assign 或以这种方式。我认为您不能使用 std::plus 强制调用 +=。
  • 这不是一个 for 循环 per se,它对复制构造函数产生了影响(std 算法也使用循环)。这是您定义 operator+ 的方式,请参阅@DaveS 的答案。
  • 您的operator+= 可能应该返回非常量引用(x += y 的结果是一个左值,通常没有理由禁止修改它)并且您的operator+ 应该可能返回一个非-const 对象(按值返回 const 对象不太适合 C++11 移动语义)。我也会让operator+ 成为非会员。

标签: c++ optimization stl transform


【解决方案1】:

Base (const T&amp; b) 不是复制构造函数,它是接受 const T&amp;Base&lt;T&gt; 的构造函数。复制构造函数通常具有签名Base(const Base&amp; )

也就是说,每次您从 int 创建一个新的 Base&lt;int&gt; 时,都会调用您的构造函数,这是您在加法运算符中执行的操作。

最后,std::transform() 使用输出迭代器赋值运算符将函数的结果赋值给输出。如果您想完全避免复制,您应该使用std::for_eachstd::bind2nd( std::mem_fun_ref(&amp;Base&lt;int&gt;::operator +=), 4 ))。这将避免制作副本,因为它将单独对引用进行操作。

【讨论】:

  • 我认为这个答案中的任何内容都不会允许他跳过副本,不是吗?
  • @MooingDuck,是的,确实如此,因为它避免使用transform,它通过修改现有对象来创建一个新对象。
  • @JonathanWakely:当我发表该评论时,第三段不存在:D 现在我可以 +1。
猜你喜欢
  • 2018-09-20
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2015-09-22
  • 2017-03-13
  • 2019-02-27
  • 1970-01-01
相关资源
最近更新 更多