9

I have a use case like the code snippet below, using map::find in a getter returned map to find a non-exist key would actually find a iterator which first value is the size of the map (likely), thus would not behave as expect, equals with map::end

this is probably because of my map was a getter returned map. And consumed the map without assign it to a variable. So that the getter returned value may have been destructed immediately.

  1. So if my guess is correct?
  2. Why it returned the size of the map instead of its end iterator?

    #include <iostream>
    #include <map>


    class B {
        long long id_;

        public:
        B() = default;
        explicit B(long long);
        ~B() = default;
    };

    B::B(long long int id) : id_(id) {}


    class A {
        std::string id_;
        std::map<long long, std::shared_ptr<B>> b_;

        public:
        A() = default;
        explicit A(std::string id);
        ~A() = default;

        const std::string &id() const;

        std::map<long long, std::shared_ptr<B>> b();

    };

    A::A(std::string id): id_(id) {
        b_[1] = std::make_shared<B>(1);
        b_[2] = std::make_shared<B>(2);
    }

    const std::string &A::id() const {
        return id_;
    }

    std::map<long long, std::shared_ptr<B>> A::b() {
        return b_;
    }


    int main() {
        std::shared_ptr<A> a = std::make_shared<A>("arst");
        if (a->b().find(3) != a->b().end()) {
            std::cout << a->b().find(3)->first << std::endl;
            std::cout << a->b().at(3) << std::endl;
        }
    }

run as below:

clang --version
Apple LLVM version 10.0.0 (clang-1000.10.44.4)
Target: x86_64-apple-darwin18.2.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

with output:

clang++ test.cc -std=c++11
./a.out
2
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: map::at:  key not found
[1]    64348 abort      ./a.out
Thrimbda
  • 93
  • 7

2 Answers2

11
std::map<long long, std::shared_ptr<B>> A::b();

You are returning the map by value, so each time you call a->b() you create a new copy of the map b_ which is why this kind of comparison:

a->b().find(3) != a->b().end()

...is undefined behavior since each call to b() returns a different map and comparing iterators from different container is undefined behavior.

Change your declaration (and definition) to return a (const-)reference:

const std::map<long long, std::shared_ptr<B>>& A::b();
Holt
  • 36,600
  • 7
  • 92
  • 139
9

Your function A::b() returns the map by value. That means a copy is returned, a unique copy each time it is called, and whose iterators are not compatible with the iterators from any other copy.

Return by reference instead.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621