Is there a way to disable a function for arguments that are temporary objects?

Viewed 219

Consider a function like this:

auto&& just(auto&& arg) { return std::forward<decltype(arg)>(arg); }

This will perfect-forward all the references and will return a dead reference when called for a temporary object.

In more details:

struct Something {};
auto something() { return Something{}; }

auto&& ref = just(something()); // ERROR: Here ref refers to already destructed Something.

auto&& hold = something();
auto&& ref = just(hold);   // OK: Here ref refers to lifetime-extended Something.

So the question is: how can one define just, so the first call will fail to compile, while the second call will compile?

1 Answers

As πάντα ῥεῖ pointed out in the comments, if you require an lvalue then you should write your function that way (single &):

[from your comment, I've used c++17 style]

template <typename Arg>
auto && just(Arg & arg);

However, I'll consider your real world problem isn't so simple and that you prefer to code the requirements inside the function:

#include <type_traits>

template <typename Arg>
auto && just(Arg && arg) {

    static_assert(std::is_lvalue_reference_v<Arg&&>);

    return std::forward<Arg>(arg);
}

Example: It's practical for a function to do some work then return the output of a function with perfect forwarding to avoid copies, but you need to check its return type. For those not able to use concepts it's neater to do the check inside the function:

#include <iostream>
#include <type_traits>

template <typename Func>
auto && just (Func && func)
{
    using return_type = decltype(func());
    
    static_assert(std::is_reference_v<return_type>, "returning temportary");

    // calculate arguments for func
    // (no args here for simplicity)

    return func(); // always return ref
}

int global;
int ReturnCopy () { return int(); }
int & ReturnGlobal () { return global; }
int && ReturnTemporary () { return std::move(global); }

int main ()
{
    just(ReturnCopy); // <--- error
    just(ReturnGlobal);
    just(ReturnTemporary);
}
Related