C ++ Strange behavior in a vector of a pair containing a reference

I just found something really weird, check this code:

#include <cstring>
#include <cstdio>
#include <utility>
#include <vector>

using namespace std;

class A {
    public:
    int n = 0;
    A(const char* p) { n = strlen(p); };

    A(A const&) = delete;
    void operator=(A const&) = delete;
};
void F(vector<pair<const char*, const A&>> v) {
    printf("F\n");
    for(vector<pair<const char*, const A&>>::iterator it = v.begin();it!=v.end();++it) printf("  '%s': %p %i\n", it->first, &it->second, it->second.n);
};

int main(int, char**) {
    F({
            { "A", "A" },
            { "B", "BB" },
            { "C", "CCC" },
            { "D", "DDDD" }
        });
};

      

Now compile it with clang++ -std=c++11 -Wall -Wextra -Wpedantic -O0 main.cc -o main

or something similar (with optimizations disabled).

And you should see the result like this:

F
  'A': 0x7fff57a0b988 1
  'B': 0x7fff57a0b9d0 2
  'C': 0x7fff57a0ba18 3
  'D': 0x7fff57a0ba60 4

      

Ok, the compiler automatically generates the vector object and related links A&

, which are all different.

Now, compile it with clang++ -std=c++11 -Wall -Wextra -Wpedantic -O1 main.cc -o main

, notice how I only added the lowest optimization level.

And you will see

F
  'A': 0x7fff5ac54b30 1629262454
  'B': 0x7fff5ac54b30 1629262454
  'C': 0x7fff5ac54b30 1629262454
  'D': 0x7fff5ac54b30 1629262454

      

that all the parameters refer to the same object A&

, which I think is wrong.

Here are my compiler details:

Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix

      

Is this the expected behavior? Is this a compiler error?

Update: As Matt McNabb pointed out, vectors and initializer_lists are not designed to work with const references (although it does compile to clang). However, when writing the same function as void F(vector<pair<char*, A&&>>) {}

, the error still persists.

+3


source to share


3 answers


Your code seems a little strange. You do it:

void F(vector<pair<const char*, const A&>> v) {

      

So you are expecting a vector with object references A

. But you don't have objects A. You are passing it string literals for which the compiler implicitly creates objects A, but they are temporary, so by the time your function body runs, they have already disappeared, and references to them are undefined behavior, so it works with - O0 but not -O1.



If you want to implicitly create objects A

and then store them, you cannot use references. Try

void F(vector<pair<const char*, const A>> v) {

      

An example is here: http://ideone.com/VNIgal

+4


source


It has to do with the constructor a pair

, which forwards links:

template<class U, class V> pair (U&& a, V&& b);

      

When the initializer_list is created and passed to the vector constructor, temporary pairs are created. Each pair is built using initializers. For example, when you create the first pair with

{ "A", "A" }

      

The compiler thinks that the best constructor matches template <typename U,typename V> pair(U&&,V&&)

where U and V are const char (&)[2]

. This means that temporary A is not created at the calling site. A temporary A is created inside the pair's constructor:

template<class U, class V> pair (U&& a, V&& b)
: first(std::forward<U>(a)), 
  second(std::forward<V>(b))
  // second is of type const A&, but std::forward<V>(b) is an rvalue
  // of type const char [2]
{
}

      



Since the argument type ( const char (&)[2])

does not match the member type ( const A &

), a temporary event is raised to initialize the member, but this temporary action only persists until the constructor completes. This means your pair remains with the dangling reference.

One way to avoid this is to explicitly create temporary resources in the calling site:

F({
        { "A", A("A") },
        { "B", A("BB") },
        { "C", A("CCC") },
        { "D", A("DDDD") }
    });

      

Now the temporary data will persist until F

it returns, and the temporary files will not be created inside the constructor pair

, since the types match.

Note that if you had a C ++ 03 form std::pair

that has no forwarding references, the behavior will be what you expect. Temporary files will be created inside main and will last until the call returns F()

.

+2


source


They do not refer to the same object A&

. They refer to different objects that have the same memory address as their lifetime does not overlap.

There is no particular expectation about whether objects with non-overlapping lifespan should or should not occupy the same memory address.

+1


source







All Articles