【问题标题】:Why didn't std::for_each + lambda work as expected?为什么 std::for_each + lambda 没有按预期工作?
【发布时间】:2014-01-10 10:27:43
【问题描述】:

这是C++ Primer 5th Edition中的一个练习:

练习 14.7:为你定义一个输出运算符 String 类你 为第 13.5 节(第 531 页)中的练习而写。(第 558 页)

我为之前的练习写的string.h

/**
 * @brief std::string like class without template
 *
 *        design:
 *
 *        [0][1][2][3][unconstructed chars][unallocated memory]
 *        ^           ^                    ^
 *        elements    first_free           cap
 */
class String
{
    friend  std::ostream& operator <<(std::ostream& os, const String& s);

public:
    //! default constructor
    String();

    //! constructor taking C-style string i.e. a char array terminated with'\0'.
    explicit String(const char * const c);

    //! copy constructor
    explicit String(const String& s);

    //! move constructor    --07.Jan.2014
    String(String&& s) noexcept;

    //! operator =
    String& operator = (const String& rhs);

    //! move operator =     --07.Jan.2014
    String& operator = (String&& rhs) noexcept;

    //! destructor
    ~String();

    //! members
    char* begin() const  { return elements;   }
    char* end()   const  { return first_free; }

    std::size_t size()     const {return first_free - elements;  }
    std::size_t capacity() const {return cap - elements;         }

private:

    //! data members
    char* elements;
    char* first_free;
    char* cap;

    std::allocator<char> alloc;

    //! utillities for big 3
    void free();    
};

std::ostream&
operator << (std::ostream& os, const String& s);

部分string.cpp

//! constructor taking C-style string i.e. a char array terminated with'\0'.
String::String(const char *  const c)
{
    auto p = c;
    char* newData = alloc.allocate(sizeof(p));

    std::uninitialized_copy(p, (p + sizeof(p)), newData);

    //! build the data structure
    elements = newData;
    cap = first_free = newData + sizeof(c);
}

std::ostream &operator <<(std::ostream &os, const String &s)
{
    std::for_each(&s.elements, &s.first_free, [&](const char* p){
        os << *p;
    });

    return os;
}

main.cpp:

#include "string.h"
#include <iostream>
int main()
{
    String s("1234");
    std::cout << s <<"\n";
    return 0;
}

输出:

1 
Press <RETURN> to close this window...

为什么输出是这样的?为什么不1234

【问题讨论】:

    标签: c++ c++11 stl lambda operator-overloading


    【解决方案1】:

    可能因为elements指向char的数组,所以每个元素都是char,而不是char*

    您还需要将&amp; 放在s.elementss.first_free 前面,因为您感兴趣的是指针指向的地址,而不是指针本身的地址。

    所以,这段代码可以工作:

    std::for_each(s.elements, s.first_free, [&](char p){
        os << p;
    });
    

    正如 @TemplateRex 在 cmets 中所提到的,使用 begin()end() 成员函数会更简洁、更惯用:

    std::for_each(s.begin(), s.end(), [&](char p){ os << p; });
    

    【讨论】:

    • std::for_each(s.begin(), s.end(), ...) 当然清晰很多
    • @TemplateRex 代码示例对于我有限的注意力范围来说太长了,所以我没有到达那里:)
    • 泄漏的抽象应该总是引起你的注意;-)
    • @juanchopanza 你是对的。但在s.elementss.first_free 之前似乎没有&amp;
    • @Alan.W 很好。我添加了一个解释为什么会这样。
    【解决方案2】:

    sizeof(pointer) 其中pointerchar const* 不返回数组的长度。你多次犯这个错误。请改用strlen。这是隐藏的,因为您的字符串长度为 4 个字符,而在 32 位系统上 sizeof(ptr) 为 4。

    下一个&amp;first_freefor_each 中类似的应该只是first_free

    接下来你的 lambda 应该采用 char 而不是 char*s。那么输出应该是&lt;&lt; p 而不是&lt;&lt; *p

    您应该同时创建const 和非const beginendconst 返回char const *,而非const 返回char*——逻辑上拥有其基础数据的容器应该使用const 这样的方式进行迭代。

    接下来替换您的for_eaxh 以使用begin()end() 就像for_each( x.begin(), x.end(), ... - 无需重做beginend 所做的事情。在 C++11 中,您甚至可以使用基于范围的 for

    for(char c : s ) {
      std::cout << c
    }
    

    而不是for_each

    【讨论】:

    • 非常感谢。如果没有您的回答,我永远不会注意到您指出的这些错误,尤其是错误使用 sizeof
    猜你喜欢
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    • 2014-08-04
    • 2021-01-03
    • 2018-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多