假设我正在制作一个函数来复制值:

template<class ItInput, class ItOutput>
void copy(ItInput i, ItOutput o) { *o = *i; }

并且我想如果io指向同一个对象,则避免分配,因为那样的话分配是没有意义的。

显然,我不能说if (i != o) { ... },因为io可能属于不同的类型,并且因为它们可能指向不同的容器(因此无法比拟)。不太明显的是,我也不能使用重载的函数模板,因为即使迭代器具有相同的类型,它们也可能属于不同的容器。

我对此的最初解决方案是:
template<class ItInput, class ItOutput>
void copy(ItInput i, ItOutput o)
{
    if (&*o != static_cast<void const *>(&*i))
        *o = *i;
}

但我不确定这是否有效。如果*o*i实际上返回一个对象而不是引用怎么办?

一般有办法吗?

最佳答案

我认为这不是真正必要的:如果分配是昂贵的,则该类型应定义一个分配操作符,该操作符执行(相对便宜)的自赋值检查以防止执行不必要的工作。但是,这是一个有趣的问题,存在很多陷阱,因此我将竭力回答。

如果我们要组装一个适用于输入和输出迭代器的通用解决方案,则必须注意以下陷阱:

  • 输入迭代器是单遍迭代器:每个元素只能通过迭代器执行一次间接操作,因此,我们无法一次通过迭代器执行间接操作以获取指向值的地址,而第二次访问执行复制。
  • 输入迭代器可以是代理迭代器。代理迭代器是一个迭代器,其operator*返回一个对象,而不是引用。使用代理迭代器时,表达式&*it的格式不正确,因为*it是一个右值(可以重载unary- &,但是这样做通常被认为是邪恶和可怕的,并且大多数类型都不这样做)。
  • 输出迭代器只能用于输出。您不能通过它执行间接操作,而不能将结果用作右值。您可以写入“指向元素”,但无法读取。

  • 因此,如果我们要进行“优化”,则仅在两个迭代器都是正向迭代器的情况下才需要进行优化(这包括双向迭代器和随机访问迭代器:它们也是正向迭代器)。

    因为我们很好,所以我们还需要注意以下事实:尽管它违反了概念要求,但是许多代理迭代器仍会错误地表示其类别,因为拥有支持序列中随机访问的代理迭代器非常有用代理对象。 (我什至不确定如果不这样做,如何实现std::vector<bool>的高效迭代器。)

    我们将使用以下标准库标题:
    #include <iterator>
    #include <type_traits>
    #include <utility>
    

    我们定义一个元函数is_forward_iterator,它测试一种类型是否为“真正的”正向迭代器(即,不是代理迭代器):
    template <typename T>
    struct is_forward_iterator :
        std::integral_constant<
            bool,
            std::is_base_of<
                std::forward_iterator_tag,
                typename std::iterator_traits<T>::iterator_category
            >::value &&
            std::is_lvalue_reference<
                decltype(*std::declval<T>())
            >::value>
    { };
    

    为简便起见,我们还定义了一个元函数can_compare,该函数测试两种类型是否均为正向迭代器:
    template <typename T, typename U>
    struct can_compare :
        std::integral_constant<
            bool,
            is_forward_iterator<T>::value &&
            is_forward_iterator<U>::value
        >
    { };
    

    然后,我们将编写copy函数的两个重载,并使用SFINAE根据迭代器类型选择正确的重载:如果两个迭代器均为正向迭代器,则将包括检查,否则将排除检查并始终执行那作业:
    template <typename InputIt, typename OutputIt>
    auto copy(InputIt const in, OutputIt const out)
        -> typename std::enable_if<can_compare<InputIt, OutputIt>::value>::type
    {
        if (static_cast<void const volatile*>(std::addressof(*in)) !=
            static_cast<void const volatile*>(std::addressof(*out)))
            *out = *in;
    }
    
    template <typename InputIt, typename OutputIt>
    auto copy(InputIt const in, OutputIt const out)
        -> typename std::enable_if<!can_compare<InputIt, OutputIt>::value>::type
    {
        *out = *in;
    }
    

    像馅饼一样简单!

    关于c++ - 是否可以测试两个迭代器是否指向同一个对象?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11661261/

    10-10 19:51
    查看更多