I'm using boost::variant
to imitate inheritance with value semantics.
There is one class that may be printed:
struct Printable { /* ... */ };
void print(const Printable &) { /* ... */ }
And class that may not:
struct NotPrintable { /* ... */ };
Finally, there is "Base
" class with implicit cast:
struct Base : boost::variant<Printable, NotPrintable>
{
Base(const Printable &) {} // constructor for implicit cast
Base(const NotPrintable &) {} // constructor for implicit cast
};
// Print, if printable, throw exception, if not
void print(const Base &base)
{
Printer printer{};
base.apply_visitor(printer);
}
The problem is how to check for printable inside of visitor:
struct Printer
{
using result_type = void;
// If printable
template<typename PrintableType> requires
requires(const PrintableType &v) { {print(v)}; } // (1)
void operator()(const PrintableType &v) { print(v); }
// If not printable
void operator()(const auto &v) { throw /*...*/; }
};
Requirement (1)
is always true due-to implicit conversion to const Base &
. How to avoid conversion only in that exact place?
I found a perfect solution that is based on Inline friend definition.
According to standard:
Such a function is implicitly an inline function (10.1.6). A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (6.4.1).
Some other things that may work:
Deleted template function
void print(auto) = delete;
cons:
Boilerplate for every function
Forbids all implicit conversions
struct Boolean;
struct String;
struct Integer
{
Integer(Boolean);
friend Integer operator+(Integer, Integer);
}
template<class T, class U>
requires (!std::convertible_to<U, T>)
void operator+(T, U) = delete;
Integer + Integer // OK
Integer + String // ERROR (as expected)
Integer + Boolean // OK
Change interface
struct Base : /* ... */
{
/* ... */
static void print(Base);
}
cons:
Base + Base
-> Base::add(Base, Base)
)template<typename T> struct Traits {
static constexpr bool is_printable = true;
}
cons:
Boolean + Integer
)?