为什么 tag_invoke 模式需要 Niebloid std::tag_invoke?

Why does tag_invoke pattern need the Niebloid std::tag_invoke at all?

此问题假定您熟悉 P1895R0 中介绍的自定义点管理技术 tag_invoke

自定义点对象可根据P1895R0定义为:

inline constexpr struct foo_cpo {
    // simplified original by omitting noexcept forward and using auto arg
    auto operator()(auto const &x) -> decltype( std::tag_invoke(*this, x) ) {
        return std::tag_invoke(*this, x); // <--^-- here are the Niebloid
    }
} foo;

但鉴于此技术的关键是直接使用对象,并将所有 ADL 委托给一个且唯一商定的标识符 tag_invoke,那么它 似乎 可以通过简单地实现相同的效果,

inline constexpr struct {
    auto operator()(auto const &x) -> decltype( tag_invoke(*this, x) ) {
        return tag_invoke(*this, x); // no Niebloid. directly ADL call tag_invoke
    }
} foo;

例如P1895R0的类型擦除示例,即https://godbolt.org/z/3TvO4f, can be reimplemented without using the Niebloid at all: https://godbolt.org/z/dzqE7b。代码与原始逐字相同,对 Niebloid std::tag_invoke 的定义取模并对所有自定义点对象使用上述 ADL 形式。

Niebloid的存在真正满足tag_invoke的要求是什么?

我不认为 tag_invoke 本身必须是一个函数对象。但是将它定义为一个对象给了我们一个方便的地方来放置 poison-pill 重载,如果我们认为这是必要的。拥有可以传递给 higher-order 函数的 first-class 公民函数通常很好。就是这样,真的。