Home > database >  Accessing the C map elements with elements passed to a function is not printing exact values
Accessing the C map elements with elements passed to a function is not printing exact values

Time:12-22

I am trying to understand the following code:

    #include <iostream>
    #include <string>
    #include <vector>
    #include <map>
    #include <utility>

    using namespace std;

    const std::string& foo(const std::pair<std::string, std::string>& a) {
    // const string& foo(const std::pair<const std::string, std::string>& a) { // *First fix*
         const std::string& strfoo = a.first;
         return strfoo;
    }

    int main() {
        std::map<std::string, std::string> m1;

        m1.insert(std::pair<std::string, std::string>(std::string("John"),std::string("Doe")));

        std::map<std::string, std::string>::iterator k = m1.begin();

        //std::pair<std::string, std::string> p1 = make_pair(k->first,k->second); // *Second Fix*

        const std::string& f = foo(*k);

        std::cout <<  "Hello " << f << " Bye";

        return 0;
     }

If I run this code (without uncommenting anything. I am using Visual Studio), I get the output as Hello Bye

Now I have added 2 comments in the code labelled as first fix and the second fix respectively.

Both of these so called fixes are printing the output as Hello John Bye

  1. First fix: If I comment the line

     const string& foo(const std::pair<std::string, std::string>& a) {
    

    and uncomment the first fix code which is making the first std::string as const, It prints the needed "Hello John Bye".

  2. Second fix: If I keep everything intact, and uncomment the second fix where I am creating a copy of the element of the map. It prints the required "Hello John Bye" when passing the pair p1 instead of dereferencing the iterator.

  3. Third fix: If I remove the reference &(ampersand) from the line

    const string& foo(const std::pair<std::string, std::string>& a) {
    

    OR

    const std::string& f = foo(*k);
    

    It also results in a copy and I get the output "Hello John Bye".

For the first one, it looks like since we are passing element of a map so we should keep the functions signature so that the key of the map should be const (I am still not sure why the code runs though)

Could someone elaborate how these fixes are working?

CodePudding user response:

map's value type is indeed std::pair<const std::string, std::string> and not std::pair<std::string, std::string>, but the latter can be converted in the former.

Applying foo on temporary returns pointer which becomes dangling at end of full expression.

1.

Changing foo signature to take exact value type avoid the creation of the problematic temporary.

  1. std::pair<std::string, std::string> p1 = make_pair(k->first,k->second);
    const std::string& f = foo(p1);
    

You no longer have temporary neither.

  1. string foo(const std::pair<std::string, std::string>& a);
    
    const std::string& f = foo(*k);
    

You no longer return reference, but a temporary. That temporary has its life extended by the bounding to const reference f.

std::string f = foo(*k);

reference return by foo is used to construct f before becoming dangling. so your are ok too.

CodePudding user response:

Case 1: Without any fixes/modifications

First note that for a map std::map<K,V>, std::map<K, V>::value_type is std::pair<const K, V>.

Second dereferencing an iterator from a std::map<K, V> will give you a std::pair<const K, V>&.

Now lets apply the above two facts to your example. In particular, the expression *k gives us

std::pair<const std::string, std::string>&

Next, the function foo in this case takes a

const std::pair<std::string, std::string>& a

Note that here the std::pair itself is const. So this means, its members are also const since the members of a const object are itself(themselves) const.

Also, this function foo returns a reference to const string. So you should get Hello John Bye as output instead of Hello Bye. My guess is that this is a bug in MSVC since in gcc and clang you get the expected(Hello John Bye) as output.

Case 2: With first fix

In this first fix, you have change your function parameter to be

const std::pair<const std::string, std::string>& a

In the above statement, you have explicitly set the first member of the std::pair as const which again should give the output Hello John Bye. In the previous case(CASE 1), the first member was implicitly const. So Case 1 and Case 2 are not really so different except that in Case 1 the second member of the passed pair is also const. You can verify this by trying to change the second member by adding the following inside foo's body in Case 1

//std::string &secondStr = a.second; //this won't work in CASE 1 because second member is implicitly const in Case 1
const std::string &secondStr = a.second; //this will work in CASE 1

Case 3: With second fix

std::pair<std::string, std::string> p1 = make_pair(k->first,k->second);
const std::string& f = foo(p1);

This time you're passing the std::pair, p1 that you created using make_pair. Again, just like Case 1, here the parameter of the function foo is const so the first and second members of the parameterwill be implicitly const and the output should be Hello John Buy.

Case 4: With third fix

string foo(const std::pair<std::string, std::string>& a);

const std::string& f = foo(*k); //f extends the lifetime of the temporary returned by foo

In this case, there is no change in the function parameter but now we're returning the string by value. Here, f extends the lifetime of the temporary returned by the call to foo and the output should be the same as before(Hello John Bye).

Summary

The output Hello Bye that you're getting in the very first case seems to be a bug in MSVC to me.

  • Related