Search code examples
c++boostboost-asioinitializer-listboost-beast

No matching constructor error (Boost Beast, C++)


Here's my header file for a WebSocket client:

namespace beast = boost::beast;
namespace asio = boost::asio;

class WebSocketClient {
 public:
  explicit WebSocketClient(asio::io_context &ioc);

 private:
  using stream_type = beast::websocket::stream<beast::ssl_stream<beast::tcp_stream>>;
  using ssl_context_type = asio::ssl::context;

  asio::ip::tcp::resolver resolver_;
  stream_type ws_;

  ssl_context_type get_ssl_context();
};

And here's my definition:

WebSocketClient::WebSocketClient(asio::io_context &ioc)
    : resolver_(asio::make_strand(ioc)),
      ws_(asio::make_strand(ioc), get_ssl_context()) {
}

asio::ssl::context WebSocketClient::get_ssl_context() {
  return WebSocketClient::ssl_context_type(asio::ssl::context_base::tlsv12_client);
}

I'm getting the error:

In template: no matching constructor for initialization of 'boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context &>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0>>, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0>>, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0>>, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0>>, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0>>>, boost::beast::unlimited_rate_policy>>' error occurred here in instantiation of function template specialization 'boost::empty_::empty_value<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::... in instantiation of function template specialization 'boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<bo... in instantiation of function template specialization 'boost::make_shared<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::execution::any_executor<boost::asio::executi... in instantiation of function template specialization 'boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::basic_stream<boost::asio::ip::tcp, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<bo... candidate constructor template not viable: expects an l-value for 2nd argument candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided

I'd be very grateful if somebody could explain why I'm getting this error? If I instead use the code below, and add another private member variable ssl_context_ of type ssl_context_type, the error goes away, but I'm not really sure why... I'm quite new to C++.

WebSocketClient::WebSocketClient(asio::io_context &ioc)
    : resolver_(asio::make_strand(ioc)),
      ssl_context_(asio::ssl::context_base::tlsv12_client),
      ws_(asio::make_strand(ioc), ssl_context_) {
}

Solution

  • The problem is that the boost::asio::ssl::stream class constructor (and by induction, beast::websocket::stream<beast::ssl_stream<beast::tcp_stream>> constructor) requires an l-value of boost::asio::ssl::context as the second parameter, and WebSocketClient::get_ssl_context returns an r-value. In other words, WebSocketClient::get_ssl_context returns a temporary, which is not acceptable for beast::websocket::stream<beast::ssl_stream<beast::tcp_stream>>.

    ssl::context needs to persist for the whole duration of the TLS stream, since the stream will be using the context during its operation. So the correct solution is to create an instance of ssl::context as a data member of WebSocketClient before ws_ and provide a reference to it to ws_ constructor.