c++templatescopy-constructorforwarding-reference

Replacing std::is_base_of_v in case of disable_if_same_or_derived


Eric Niebler wrote some time ago an article about Universal References and the Copy Constructor:

Niebler

The solution at the end is

template <typename A, typename B>
using disable_if_same_or_derived = std::enable_if_t<!std::is_base_of_v<A, std::remove_reference_t<B>>>;

The downside of this is that std::is_base_of_v requires the full definition of the type parameter. Using this with a forwarded type is impossible.

I have now c++20 at my disposal and wonder if we can come up with a solution to this. Do you have any idea?


Solution

  • Add a tag type, like std::piecewise_construct_t or the like, that marks a perfect forwarding constructor as a non-copy/move constructor.

    std::pair<Foo, Foo> p2(std::piecewise_construct, t, t)
    

    Then when working with such a type you can prefix the constructor call with std::piecewise_construct_t or similar.

    The idea of these tag types is that they are "instructions" for the constructor to follow. And as they are complete, tests of the first argument for "is base of" will complete.

    It is very rarely the case you actually should have a class that forwards its constructor arguments to somewhere else opaque without it having special meaning. Even a tuple; we are constructing the contents of the tuple even in the case of a tuple of one element.

    Once we have that "wrapping" meaning, we can then express it as as a tag instruction. That tag instruction can be explicit, and it doesn't do magic and detect what the properties of its other arguments are. This permits incomplete types to be passed through without having to know how they work.

    Generally it is never safe to detect "is this type incomplete" and behave differently, because the C++ standard is worded such that any template that behaves differently for complete and incomplete types in the same compilation unit results in an ill-formed program, no diagnostic required; even between compilation units things don't generally work due to the one definition rule.