Use placement new to start the lifetime of an endpoint object in provided storage.
After you're done with the object you should later call its destructor to formally end its lifetime. This is not strictly a requirement, but it's often important because destructors may return resources to the system. For example:
#include <iostream>
#include <memory>
#include <type_traits>
#include <boost/asio.hpp>
#include <boost/asio/ts/buffer.hpp>
#include <boost/asio/ts/internet.hpp>
namespace asio = boost::asio;
namespace ip = asio::ip;
template <typename T> using aligned_storage_for =
std::aligned_storage_t<sizeof(T), alignof(T)>;
int main()
{
boost::system::error_code ec;
asio::io_context ctx;
aligned_storage_for<ip::tcp::endpoint> buf_endpoint;
aligned_storage_for<ip::tcp::socket> buf_socket;
auto pendpoint = reinterpret_cast<ip::tcp::endpoint*>(&buf_endpoint);
auto psocket = reinterpret_cast<ip::tcp::socket*> (&buf_socket);
// Start the lifetime of an endpoint in the storage pointed to by pendpoint
// We have guaranteed that the pointed-to memory block is
// - large enough to store an endpoint object; and
// - suitably aligned for the endpoint object.
// Next, do the same for the socket.
new (pendpoint) ip::tcp::endpoint(asio::ip::make_address("127.0.0.1"), 80);
new (psocket) ip::tcp::socket(ctx);
psocket->connect(*pendpoint, ec);
// typical C idiom: clean up in reverse order of initialization
// Ends the lifetime of the objects
psocket->ip::tcp::socket::~socket();
pendpoint->ip::tcp::endpoint::~endpoint();
// If we dynamically allocated storage with malloc or new, this is the location
// to release it
}