假设我正在制作一个函数来复制值:
template<class ItInput, class ItOutput>
void copy(ItInput i, ItOutput o) { *o = *i; }
并且我想如果
i
和o
指向同一个对象,则避免分配,因为那样的话分配是没有意义的。显然,我不能说
if (i != o) { ... }
,因为i
和o
可能属于不同的类型,并且因为它们可能指向不同的容器(因此无法比拟)。不太明显的是,我也不能使用重载的函数模板,因为即使迭代器具有相同的类型,它们也可能属于不同的容器。我对此的最初解决方案是:
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/