Currently I have this code, which works fine:
#pragma once
#include <expected>
#include <wayland-server.h>
class Display
{
private:
struct wl_display *ptr;
public:
enum class Error
{
CannotCreateDisplay,
};
Display() = delete;
Display(struct wl_display *ptr)
: ptr{ptr}
{
}
Display(const Display &) = delete;
Display(Display &&) = delete;
Display &operator=(const Display &) = delete;
Display &operator=(Display &&) = delete;
~Display()
{
wl_display_destroy_clients(this->ptr);
wl_display_destroy(this->ptr);
}
static std::expected<Display, Error> create()
{
struct wl_display *ptr{wl_display_create()};
if (ptr == nullptr) {
return std::unexpected{Error::CannotCreateDisplay};
}
return {ptr};
}
};
I would like to make the Display(struct wl_display *) constructor private but I can't because otherwise the create would complains about not finding any viable constructor to call for Display.
One solution I found, is to make a move constructor for Display but is there any else solutions for this ?
class Display
{
private:
struct wl_display *ptr;
Display(struct wl_display *ptr)
: ptr{ptr}
{
}
public:
enum class Error
{
CannotCreateDisplay,
};
Display() = delete;
Display(const Display &) = delete;
Display(Display &&) = delete;
Display &operator=(const Display &) = delete;
Display &operator=(Display &&) = delete;
~Display()
{
wl_display_destroy_clients(this->ptr);
wl_display_destroy(this->ptr);
}
static std::expected<Display, Error> create()
{
struct wl_display *ptr{wl_display_create()};
if (ptr == nullptr) {
return std::unexpected{Error::CannotCreateDisplay};
}
return {ptr}; // ERROR: can't find any viable constructor
}
};
Surely you can, by making use of the monadic operations of the
std::expected. The same method can be applied tostd::optional.Instead of
You can detour via another
std::expected, and transform the value in-place (credit to @Artyer for making it more succinct):Demo: https://godbolt.org/z/x4aoWsTeT
Here is how it works. Without the detour, you have to choose between constructing
Displaydirectly insidestd::expected, or constructingDisplayoutside and move it.The former requires invoking the private constructor directly inside the constructor of
std::expected. Sincestd::expectedis not a friend, this is doomed to fail.The latter only works if
Displayis movable. This is exactly what OP wants to avoid.With the detour, we are providing a (sort of public) callback function which calls the private constructor in a context that it is allowed. This is similar to the passkey idiom mentioned in the comments: both provide a public construction function that has very limited access. In the passkey idiom, the access is protected by a struct that serves as an authorization token; In this method, the access is protected naturally by scope, as the lambda is a local variable.