c++ iter_swap可以被特殊化吗?

s71maibg  于 2023-02-26  发布在  其他
关注(0)|答案(4)|浏览(149)

如果我有一个容器std::vector<T*> items,我可以创建一个IndirectIterator,它 Package 了std::vector<T*>::iterator,并允许在T而不是T*上迭代。
我是否可以将iter_swap专用于IndirectIterator,以使标准算法(如std::sort)通过指针交换项?
也就是说,如果我写下面的代码,它会对标准算法产生影响吗?

namespace some_namespace
{
    template <typename IterT>
    class IndirectIterator
    {
            IterT m_base;
        public:
            typedef IterT base_iterator;
            typedef /* ... */ reference;

            /* ... */

            reference operator*() const { **m_base; }

            const base_iterator& base() const { return m_base; }
            base_iterator& base() { return m_base; }
    };

    template <typename T>
    void iter_swap(IndirectIterator<T>& a, IndirectIterator<T>& b)
    {
        using std::iter_swap;
        iter_swap(a.base(), b.base());
    }
}

这种特殊化的好处是它交换指针而不是完整的T示例,因此它更快(潜在地)。

eulz3vhy

eulz3vhy1#

据我所知,iter_swap只在std::reverse中使用,并且它没有提到任何类型的依赖于参数的查找:它总是使用std::iter_swap,而且因为你不允许重载std命名空间中的函数,所以你不太走运。

cwtwac6a

cwtwac6a2#

我是否可以将iter_swap专用于IndirectIterator,以使标准算法(如std::sort)通过指针交换项?
你总是可以进行重载/特化,但是你的问题是你是否可以在命名空间std内特化iter_swap
我认为标准中的答案并不明确,我发现在某些情况下,我不得不在std中定义一个特殊的iter_swap,以便std::sort使用它。在gcc标准库中,std::sort使用限定的std::iter_swap
这可能是std::sort中的一个缺陷。IMO std::sort应该调用一个不合格的swap_iter
也就是说,如果我写下面的代码,它会对标准算法产生影响吗?
至少在GCC中没有,因为标准算法使用合格的std::iter_swap(bug?)我认为标准对此并不清楚。

wmomyfyw

wmomyfyw3#

你可以重新打开std命名空间,并在std中专门化模板,只要你专门化它们为用户定义的类型。在你的情况下,你实际上可以专门化std::iter_swap为你的目的,只要确保你是在std命名空间中做的,而不是在你自己的命名空间中(如你的例子)。这不是很优雅,但这是允许的。

9gm1akwq

9gm1akwq4#

从C++20开始,答案是明确的“是的,你 * 可以 * 定制你自己的ADL iter_swap”。STL算法 * 应该 * 使用它,但不是必须的,而且在实践中存在实现分歧。
为了定制一个定制点,你应该使用“隐藏朋友习惯用法”:

namespace some_namespace {
    template <class IterT>
    class IndirectIterator {
        IterT m_base;
    public:
        /* ... */

        reference operator*() const { return **m_base; }
        const base_iterator& base() const { return m_base; }

        // "It's not a member, it's just a friend" 
        friend void iter_swap(IndirectIterator a, IndirectIterator b) {
            using std::iter_swap;
            iter_swap(a.base(), b.base());
        }
    };
}

这里有细微的变化:你的iter_swap已经通过可变引用获取了它的参数!这是不起作用的,例如,如果它被传递了一个常量IndirectIterator,或者一个右值IndirectIterator。迭代器参数应该总是通过值来获取。同样,提供一个非常量版本的base()是没有意义的。你的base()是一个getter,而不是setter;由于X1 M5 N1 X是常数成员函数的相同原因,它应该是常数成员函数。
最后,这里最大的警告是,你绝不能定制iter_swap来做任何 * 明显不同于 * 交换迭代器目标的事情。如果你这样做了,那么它就是未定义的行为--所有的赌注都是无效的--程序可以做任何事情。正式地说,这个要求在[iterator.cust.swap]中给出:
如果重载解决方案选择的[iter_swap]函数不交换 E1E2 表示的值,则程序是病态的,不需要诊断。
我相信让iter_swap交换“第一级”目标而不是“第二级”目标的想法是完全正确的,只要你永远不会依赖于观察差异。标准库仍然 * 被允许 * 绕过iter_swap,如果它喜欢的话,只做swap(*it, *kt);你的代码必须能够科普这个问题,否则“程序是病态的,不需要诊断”(也就是说,你必须完全相信交换这些第一级目标等同于“交换 E1E2 的值”,因此可以与任何其他交换值的方法互换。你不能“依赖"你自己的方法被使用)。
Here is a complete C++20 example on Godbolt.截至2023年2月,我在这里看到定制的iter_swap实际上正在使用:
| 算法|库标准数据库++|libc++|微软|
| - ------|- ------|- ------|- ------|
| std::ranges::partition|是的|是的|是的|
| std::ranges::sort|没有|没有|是的|
| std::partition|没有|没有|没有|
| std::sort|没有|没有|没有|

相关问题