Why Do We Need std::addressof, If We Already Have The & Operator?

At first glance, the std::addressof method appears to be redundant. Its  output is the address of the object passed to it. It’s exactly what the & operator have been doing ever since C was developed in the early 70’s:

#include <iostream>
#include <memory>

template<typename T>
void getAddress(T t)
{
	assert( std::addressof(t) == &t);
	std::cout << "Address returned by & operator: " << std::ios::hex << &t << "\n";
	std::cout << "Address returned by addressof: " << std::ios::hex << std::addressof(t) << "\n";
}

int main()
{
	int i = 0;
	std::cout << "Getting the address of an int type \n";
	getAddress(i);
}

Getting the address of an int type:
Address returned by & operator: 20480024FC1C
Address returned by addressof: 20480024FC1C

C , however, don’t  allow operator overloading while C++ do. More specifically C++ will let you overload the & operator.  Consider instantiating the getAddress template function with the  Buffer class defined below:

#include <iostream>
#include <memory>

class Buffer
{
private:
	static const size_t buffer_size = 256;
	int bufferId;
	char buffer[buffer_size];

public:
	Buffer(int bufferId_) : bufferId(bufferId_) {}
	Buffer* operator&() { return reinterpret_cast<Buffer*> (&buffer); }  //BAD practice, only for  illustration!
};

template<typename T>
void getAddress(T t)
{
	std::cout << "Address returned by & operator: " << std::ios::hex << &t << "\n";
	std::cout << "Address returned by addressof: " << std::ios::hex << std::addressof(t) << "\n";
}

int main()
{
	Buffer b(1);
	std::cout << "Getting the address of a Buffer type: \n";
	getAddress(b);
}

Getting the address of a Buffer type:
Address returned by & operator: 20480078F50C
Address returned by addressof: 20480078F508

In the Buffer class, the & operator is (badly) overloaded and coerced to return the address of the internal  buffer as the address of the  object. The template implementation expecting a type with an accessible & operator which, it assumes,  returns the address of the object, report the wrong address.  At the same time std::addressof method returns accurately the object’s  address.

So this is why we need std::addressof. It  was introduced into  the boost library and later into the C++ 11 standard as a workaround for a potentially unreliable & operator.

When should you use the std::addressof  method?

You should use std::addressof instead of & in any template implementation taking the address of a user-defined object. Doing so is not a perfect solution as it might  cause unexpected  behavior with types relying on an overloaded & operator (some argue it is an appropriate and just  punishment for developers dabbling in this dark art) .  However, it is possible to  detect the conflict of address reporting and  template expectations in debug builds  with assert( std::addressof(t) == &t) :

template<typename T>
void getAddress(T t)
{
	assert( std::addressof(t) == &t);
	std::cout << "Address returned by & operator: " << std::ios::hex << &t << "\n";
	std::cout << "Address returned by addressof: " << std::ios::hex << std::addressof(t) << "\n";
}

This way you will at least get a heads up while you develop and test your code.

Conclusion

Even prior to C++11 ,  Overloading the & operator has been discouraged, frowned upon  and even prohibited by several C++ coding guidelines such as the one used in aircraft and medical devices software development:   MISRA-C++:2008  (section 5-3-3).  A boost library version of addressof was released in  2002 suggesting that overloading the address operator was perceived as a problem for a long while before the C++ 11 makeover. Yet, the  introduction of the std::addressof  method  into the C++11  standard  is evidence that  the C++ committee  feared too much legacy code will break if the & operator no longer allowed to be overloaded.  I guess this is one case where the jokes about C++ giving you the tools to shoot yourself in the foot are right on the nose.

Raw pointers are also Iterators!

Using raw pointers (a.k.a. naked pointers) is never a good first choice. Being high maintenance and error prone creatures, you’re far better off using one of the alternatives offered by the standard library.
Having said that, sometimes you simply do not have a choice. You might be working with a block of memory passed on by a hardware component, or you might be stuck with some critical legacy library that “just works” and nobody dares touch today.
In Any case, using  naked pointer doesn’t mean you have to go full commando yourself. Many of the STL algorithms would accept, with little or no complaints,  raw pointers. an iterator is, after all , a  generalized  pointer:

#include <iostream>
#include <algorithm>
#include <array>
using namespace std;


void PrintArray( int* pBegin, int* pEnd)
{
	cout << *pBegin++;
	for_each(pBegin, pEnd,[](int i) { cout << ',' << i; } );
	cout << endl << endl;
}

int main()
{
	// an integer array
	const auto integer_list =  { 19,14,3,5,20,11,10,18,1,17,9,6,7,2,13,12,4,16,8,15 };
	
	const size_t block_size = integer_list.size();
	int* pMemBlock = new int[block_size];
	int* pBegin = pMemBlock;
	int* pEnd = (pBegin + block_size);

	//copy the content of the initializer list to the memory block 
	std::copy(integer_list.begin(), integer_list.end(), pBegin);
	cout << "The list of numbers in the memory block:" << endl;
	PrintArray(pBegin, pEnd);

	int count = count_if(pBegin, pEnd, [](int n) {return  n < 5; });
	cout << count  <<  " numbers are less than 5\n" <<  endl;
	
	std::sort(pBegin, pEnd);
	cout << "sorted list:" << endl;
	PrintArray(pBegin, pEnd);
	cout << endl;
	
	random_shuffle(pBegin, pEnd);

	cout << "shuffled  list:" << endl;
	PrintArray(pBegin, pEnd);

	delete[] pMemBlock;
}

The list of numbers in the memory block:
19,14,3,5,20,11,10,18,1,17,9,6,7,2,13,12,4,16,8,15

4 numbers are less than 5

sorted list:
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20

shuffled list:
5,11,12,16,15,17,18,2,7,10,4,8,20,3,1,13,6,19,14,9

Since the array size is not known at compile time, it’s up to you to accurately calculate the begin and end  pointers (lines 22,23) ,  once these pointers contain the proper addresses,  they can be passed around  just like any other iterator.

note:  if it was a static array, it would have been possible to apply the begin() and end() functions  (overloaded for static arrays by the standard library):

	int arr[]{ 28,4,26,21,20,22,11,12,14,8,17,30,13,15,9,7,19,1,16 };
	cout << "The list of numbers in the static array :" << endl;
	PrintArray(begin(arr), end(arr));