The compiler can decide to implement references as pointers, or inlining or any other method it chooses to use. In terms of performance, it's irrelevant. The compiler can and will do whatever it wants to when it comes to optimization. The compiler can implement your reference as a pass-by-value if it wants to (and if it's valid to do so in the specific situation).
Caching the result won't help because the compiler will do that anyways.
If you want to explicitly tell the compiler that the value might change (because of another thread that has access to the same pointer), you need to use the keyword volatile (or std::atomic if you're not already using a std::mutex).
Edit: The keyword "volatile" is never required for multithreading. std::mutex is enough.
If you don't use the keyword volatile, the compiler will almost certainly cache the result for you (if appropriate).
There are, however, at least 2 actual differences in the rules between pointers and references.
- Taking the address (pointer) of a temporary value (rvalue) is undefined behavior in C++.
- References are immutable, sometimes need to be wrapped in std::ref.
Here I'll provide examples for both differences.
This code using references is valid:
static int do_stuff(const int& i)
{
}
int main()
{
do_stuff(5);
return 0;
}
But this code has undefined behavior (in practice it will probably still work):
static int do_stuff(const int* i)
{
}
int main()
{
do_stuff(&5);
return 0;
}
That's because taking the address of a temporary value (non lvalue) is undefined behavior in C++. The value is not guaranteed to have an address. Note that taking the address like this is valid:
static int do_stuff(const int& i)
{
const int *ptr = &i;
}
int main()
{
do_stuff(5);
return 0;
}
Because inside of the function do_stuff, the variable has a name and is therefore an lvalue. That means that by the time it's inside of do_stuff it's guaranteed to have an address.
So that's one difference between a pointer an a reference in C++.
There is another difference, and that is the constness / immutability.
On important thing to know about in C++ is the use for the helper function std::ref.
Consider the following code:
#include <functional>
#include <thread>
#include <future>
#include <chrono>
#include <iostream>
struct important_t
{
int val = 0;
};
static void work(const volatile important_t& arg)
{
std::cout << "Doing work..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
}
int main()
{
important_t my_object;
{
std::cout << "Starting thread" << std::endl;
std::future<void> t = std::async(std::launch::async, work, std::ref(my_object));
std::cout << "Waiting for thread to finish" << std::endl;
}
return 0;
}
The above code will compile just fine, and is perfectly valid C++ code.
But if you wrote it like this:
std::future<void> t = std::async(std::launch::async, work, my_object);
It wouldn't compile. That's because of the std::ref.
The reason that the code doesn't compile without std::ref is that the function std::async (and also std::thread) requires each and every one of the objects being passed as function parameters to be copy constructible.
That demonstrates a fundamental difference between references and all other built-in types in C++. References are immutable, and there's no way to make them editable.
Consider the following code:
#include <iostream>
int main()
{
// Perfectly valid
// Prints 5
{
int val = 0;
int& val_ref = val;
val_ref = 5;
std::cout << val << std::endl;
}
// Compiler error:
// A reference must always be initialized.
// A reference will always point to the same value throughout its lifetime.
{
int val = 0;
int& val_ref;
val_ref = val;
val_ref = 5;
std::cout << val << std::endl;
}
// We will encounter a similar compiler error with a const pointer:
// A const value must always be initialized.
// A const pointer will always point to the same value throughout its lifetime.
{
int val = 0;
int *const val_ptr;
val_ref = &val;
val_ref = 5;
std::cout << val << std::endl;
}
return 0;
}
That leads to the conclusion that a reference is not the same thing as a pointer in C++. It's almost the same thing as a const pointer.
Just a little bit of clarification:
A const pointer to a const int:
void do_stuff(const int *const val)
{
int i;
val = 5; // Error
val = &i; // Error
}
A const pointer to an int:
void do_stuff(int *const val)
{
int i;
val = 5; // Allowed. The int is not const.
val = &i; // Error
}
A pointer to a const int:
void do_stuff(const int* val)
{
int i;
val = 5; // Error
val = &i; // Allowed
}
An int reference in C++ is the closest thing to a const pointer to an int. The int is editable, the pointer is not.