Alexey
Alexey

Reputation: 1226

Variadic templates with const references

My project uses custom allocator that constructs objects the following way:

template <typename T, typename... Args>
T* create(Args... args)
{
    T* object = // request "bare" memory
    // call object constructor
    return new(reinterpret_cast<void*>(object)) T(args...);
}

There is a class, containing const reference as a field:

struct A {
  A(const string& s) : str_(s) {
    cout << &s << '\n';
  }

  const string& str_;
};

But when I'm trying to create an object, I'm getting wrong results:

string* str = new string("some string");
cout << str << '\n';
A* a = create<A>(*str);

The output:

0x7fffc8004db4
0x7fffd5436050

I thought the const reference field (str_) should contain the same address, that has been given to construction machinery, but obviously it isn't true.

Why is it happen, and how to avoid it?

Ofc, I can not help using custom allocators, it is mandatory, I wouldn't ask.

Upvotes: 4

Views: 3901

Answers (2)

R Sahu
R Sahu

Reputation: 206607

The call

A* a = create<A>(*str);

creates a copy of *str and uses the copy to call the function since the function is declared as

template <typename T, typename... Args>
T* create(Args... args) {...}

a has a reference to an object that is no longer alive.

Use

template <typename T, typename... Args>
T* create(Args&&... args)
{
   T* object = // request "bare" memory
   // call object constructor
   return new(reinterpret_cast<void*>(object)) T(std::forward<Args>(args)...));
}

instead.

Upvotes: 2

WhiZTiM
WhiZTiM

Reputation: 21576

template <typename T, typename... Args>
T* create(Args... args)
{
    T* object = // request "bare" memory
    // call object constructor
    return new(reinterpret_cast<void*>(object)) T(args...);
}

All your variadic arguments will be passed by value. Hence a copy is being made. You probably want to use forwarding references:

template <typename T, typename... Args>
T* create(Args&&... args)
{
    auto memory =  // request "bare" memory
    // call object constructor
    return new(memory) T(std::forward<Args>(args)...);
}

Prints:

0x111ac20
0x111ac20

Demo


Again consider data alignment whenever you want to use placement-new.

Upvotes: 4

Related Questions