Skip to content

Instantly share code, notes, and snippets.

@oblitum oblitum/lvalue.md
Last active Mar 9, 2019

Embed
What would you like to do?
A rvalue to lvalue conversion function

#Opinions on rvalue to lvalue conversion function

Recently I've discovered that sometimes being able to turn rvalues temporarily into lvalues can be useful for me.

I've been using the following tool:

template <typename T>
constexpr T &lvalue(T &&r) noexcept { return r; }

It's useful when you have to use functions that require lvalues as arguments, but you don't have any interest in what those particular values get changed into. For when you are interested in other output vectors that are not related to the given specific argument.

For example, this:

std::string get_my_file() {
    std::ifstream ifs("myfile.txt");
    return {std::istreambuf_iterator<char>(ifs),
            std::istreambuf_iterator<char>()};
}

can be changed to this:

std::string get_my_file() {
    return {std::istreambuf_iterator<char>(lvalue(std::ifstream("myfile.txt"))),
            std::istreambuf_iterator<char>()};
}

And this:

std::string temp1 = get_my_shader();
const char *temp2 = temp1.c_str();
glShaderSource(a, 1, &temp2, nullptr);

can be changed to this:

glShaderSource(a, 1, &lvalue(get_my_shader().c_str()), nullptr);

There's a recent change to the standard about reinterpret_cast and xvalues that seems to be on topic:

http://stackoverflow.com/a/26793404/1000282

@marcodiiga

This comment has been minimized.

Copy link

marcodiiga commented Dec 1, 2016

Watch out: your code can invoke UB

struct test {
  test() {
      std::cout << "constructing" << std::endl;
  }
  ~test() {
      std::cout << "destroy" << std::endl;
  }
};
 
template <typename T>
constexpr T &lvalue(T &&r) noexcept { return r; }
 
int main()
{
  int ten = 10;
  test& ref = lvalue(test());
  std::cout << "now use" << std::endl;
  // Using ref is UB
}

it is safe to use as long as the temporaries aren't destructed until the end of the full expression and you're done dealing with them by that time.

@raleighlittles

This comment has been minimized.

Copy link

raleighlittles commented Mar 9, 2019

Thanks for sharing this! I ran into the same issue recently. While your solution works, I found that you can also achieve the same thing directly using (std::add_lvalue_reference)[http://www.cplusplus.com/reference/type_traits/add_lvalue_reference/] and std::forward

Here's two functions:

void func(int & a)
{
  std::cout << "1) " << a << std::endl;
}

void func(int &&a )
{
  std::cout << "2)" << a << std::endl;
}

The first version takes the regular integer reference and the 2nd version takes the fancy rvalue reference.

If you want to call the second version with the argument being created inline, you can do:

func(std::forward<int>(0)), but to call the first version, you need to do:

func( std::add_lvalue_reference<int>::type(std::forward<int>(0)));

Which is equivalent to doing func(lvalue<int>(0)) using your solution.

I believe that the std::forward implementation here requires C++14 and not just C++11, however.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.