我不认为这真的有必要:如果赋值很昂贵,则该类型应该定义一个赋值运算符来执行(相对便宜的)自赋值检查,以防止做不必要的工作。但是,这是一个有趣的问题,有很多陷阱,所以我会尝试回答它。
如果我们要组装一个适用于输入和输出迭代器的通用解决方案,我们必须注意几个陷阱:
输入迭代器是单遍迭代器:每个元素只能通过迭代器执行一次间接,因此,我们不能通过迭代器执行一次间接获取指向值的地址,然后再执行第二次副本。
输入迭代器可以是代理迭代器。代理迭代器是一个迭代器,它operator*
返回一个对象,而不是一个引用。使用代理迭代器,表达式&*it
是不正确的,因为*it
它是一个右值(可能会重载一元- &
,但这样做通常被认为是邪恶和可怕的,大多数类型都不会这样做)。
一个输出迭代器只能用于输出;您不能通过它执行间接并将结果用作右值。您可以写入“指向元素”,但不能从中读取。
因此,如果我们要进行“优化”,我们只需要在两个迭代器都是前向迭代器的情况下进行优化(这包括双向迭代器和随机访问迭代器:它们也是前向迭代器)。
因为我们很好,我们还需要注意这样一个事实,尽管它违反了概念要求,但许多代理迭代器歪曲了他们的类别,因为拥有一个支持随机访问序列的代理迭代器非常有用代理对象。(我什至不确定如何在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;
}
像馅饼一样容易!