Pass pointer to temporary in c++ 11?

Viewed 3658

I have an existing function:

void foo(const Key* key = nullptr)
{
  // uses the key
}

I want to pass it pointer to temporary Key object (i.e. rvalue) like:

foo(&Key());

This causes compilation error, but is there a way in c++ 11/14 how I can do this? Of course I could do:

Key key;
foo(&key);

But I don't need object Key, I only need it inside foo() and foo()

Or I could do:

foo(new Key());

But then the object will not be deleted.

7 Answers

I don't think this is a good idea, but if you really really want a temporary and cannot change foo, you can cast the temporary to a const&:

int main()
{
    foo(&static_cast<const Key&>(Key{}));
}

live example on wandbox


Alternatively, you could "hide" the creation of the object behind a convenient function:

template <typename T, typename F, typename... Ts>
void invoke_with_temporary(F&& f, Ts&&... xs)
{
    T obj{std::forward<Ts>(xs)...};
    f(&obj);
}

int main()
{
    invoke_with_temporary<Key>(&foo);
}

live example on wandbox.org


Another alternative: provide an overload of foo that takes a reference:

void foo(Key&& key)
{
    foo(&key);
}

Just control the scope of the throw-away variable yourself:

{
    Key key;
    foo(&key);
} // <-- 'key' is destroyed here

This is a utilty function. It is basically the inverse of std::move1:

template<class T>
T& as_lvalue( T&& t ) { return t; }

if used wrong it can lead to dangling references.

Your code then becomes:

foo(&as_lvalue(Key()));

the goal of "cannot take an address of a temporary" is because you can otherwise get extremely unexpected behaviour due to things like implicit temporary creation.

In this case, we are explicitly taking the address of a temporary.

It is no more dangerous than creating a named value, calling the function, and then discarding the named value immediately.


1 std::move takes an l or r value and returns a rvalue reference to it, indicating that consumer should treat it as a temporary whose existence will be shortly discarded. as_lvalue takes an l or r value reference and returns an lvalue reference to it, indicating that the consumer should treat it as a non-temporary whose existence will persist.

They are both valid operations, but std::move is more crucial. (std::move could be called as_rvalue really). I'd advise against clever names like unmove.

The only reason to take a pointer rather than a reference is if the pointer can be null. And if that's the case, then your foo function will look something like this:

void foo(const Key *key)
{
  if(key)
    //Do stuff with `key`
  else
    //Alternate code
}

Given that, what you want is a second overload, one that refactors all of the "do stuff with key" into its own function that takes a reference. So do that:

void foo(const Key &key)
{
  //Do stuff with `key`
}

void foo(const Key *key)
{
  if(key)
    foo(*key);
  else
    //Alternate code.
}

If you have common code that gets executed in both cases, refactor that out into its own function too.

Yes, it's possible that "do stuff with key" is complicated and is split up into several lines. But that suggests a very strange design here.

You could also refactor it the other way:

void foo(const Key &key) {foo(&key);}

I use something like this:

template <typename T>
const T* temp_ptr(const T&& x) { return &x; }

Used like this:

foo(temp_ptr(Key{}));

This is very useful when dealing with certain legacy APIs. DirectX 11 in particular frequently takes parameter aggregating structs by const T* and it's convenient to create and pass them inline. I don't think there's anything wrong with this idiom unlike some of the commenters here, although I'd prefer if those APIs just took a const reference and handled optional arguments differently.

Here's an example D3D11 API call where this is very useful:

    vector<Vec3f> verts;
    ...
    ID3D11BufferPtr vbuf;
    d3dDevice->CreateBuffer(
        temp_ptr(CD3D11_BUFFER_DESC{byteSize(verts), D3D11_BIND_VERTEX_BUFFER}),
        temp_ptr(D3D11_SUBRESOURCE_DATA{data(verts)}), &vbuf);

For calling ID3D11Device::CreateBuffer() to create a vertex buffer.

On larger projects I might write wrappers for many of the D3D API calls which make them more convenient to call in a modern C++ style but for small standalone sample projects that I want to have minimum extra code or dependencies I find this very useful.

Another trick I've used in the past that works is:

foo(std::data({Key{}}));

But I don't particularly recommend this as I think the intent is unclear and relies on a bit too much knowledge of how initializer lists work. A variation is useful if you need to pass a temporary 'array' though:

d3dDeviceContext->ClearRenderTargetView(rendertarget, data({0.0f, 0.0f, 0.0f, 0.0f}));

For calling an API like ID3D11DeviceContext::ClearRenderTargetView().

if what you concern is variable scope (as you mention in comment) you can use

{Key key;foo(&key);}

Or I could do:

foo(new Key());

But then the object will not be deleted.

You could create a smart pointer, but I'd rather not. It still is a possibility though.

#include <memory>
foo(std::make_unique<const Key>().get());
Related