tag_invoke#

#include <dplx/cncr/tag_invoke.hpp>
namespace dplx::cncr {}
template<typename T, typename ...TArgs>
constexpr decltype(auto) tag_invoke(T policy, TArgs&&... args)#

A helper niebloid for writing customization point objects (CPO) which share the common function name tag_invoke for the user provided function. The different customization points are distinguished by the policy argument which ought to be the invoking CPO.

The user provided customizations can live in the CPO namespace, the target type’s namespace or be inline friend functions which is the preferred way (see the example below). Constrained default implementations can be either embedded within the CPO or the CPO namespace.

This niebloid invokes the name tag_invoke via ADL and perfectly forwards all arguments, the return value and noexcept specification.

See also

The original proposal P1895

Here is an example definition of a tag_invoke based CPO:

inline constexpr struct write_fn
{
    template <typename Stream>
        requires tag_invocable<write_fn,
                            Stream &,
                            std::byte const *,
                            std::size_t const>
    auto operator()(Stream &stream,
                    std::byte const *bytes,
                    std::size_t const numBytes) const
            noexcept(nothrow_tag_invocable<write_fn,
                                        Stream &,
                                        std::byte const *,
                                        std::size_t const>)
                    -> tag_invoke_result_t<write_fn,
                                        Stream &,
                                        std::byte const *,
                                        std::size_t const>
    {
        return cncr::tag_invoke(*this, stream, bytes, numBytes);
    }
} write{};

The above CPO could be specialized like so:

class some_stream
{
    /*...*/
    inline friend void tag_invoke(cncr::tag_t<write>,
                                  some_stream &,
                                  std::byte const *bytes,
                                  std::size_t const numBytes)
    {
        /* the customized behavior */
    }
    /*...*/
};

As the CPO definition is quite involved, the following snippet might be of use:

inline constexpr struct __CPO_NAME___fn
{
    template <typename Stream>
        requires tag_invocable<__CPO_NAME___fn,
                            __CPO_TYPES__>
    auto operator()(__CPO_ARGS__) const
            noexcept(nothrow_tag_invocable<__CPO_NAME___fn,
                                        __CPO_TYPES__>)
                    -> tag_invoke_result_t<__CPO_NAME___fn,
                                        __CPO_TYPES__>
    {
        return cncr::tag_invoke(*this, __CPO_ARG_NAMES__);
    }
} __CPO_NAME__{};
template<auto &Tag>
using tag_t#

Retrieves the underlying type of a CPO usable for implementing a tag_invoke overload for that CPO.

template<typename Tag, typename ...TArgs>
concept tag_invocable = std::invocable<decltype(cncr::tag_invoke), Tag, TArgs...>#

The concept is satisfied if and only if the CPO identified by Tag is implemented for the given set of arguments. It should only be used as a building block for concepts which (partially) consist of CPO constraints.

template<typename Tag, typename ...TArgs>
concept nothrow_tag_invocable#

Satisfied if tag_invocable<Tag, TArgs...> holds and the invoked tag_invoke overload is noexcept.

template<typename Tag, typename ...TArgs>
using tag_invoke_result_t#

A type meta function computing the return type of the invoked CPO.