为什么传递给函数 set::itrator 而不是 const_iterator 违反了一个定义规则?



cppreference.com 给出的std::set容器的描述在末尾包含以下注释:

成员类型iteratorconst_iterator可以是同一类型的别名。由于iterator可以转换为const_iterator,因此const_iterator应该在函数参数列表中使用,以避免违反一个定义规则。

我不明白最后这句话。我的理解是,一个集合不允许修改它的元素(如果你需要更改一个,你必须erase它,然后insert新的),所以每个iterator都像一个const_iterator。该标准补充说,它们可能是(但不是必需的)是同一类型。到目前为止很清楚。

我没有得到的是可能违反一个定义规则。该规则表示,一个函数可以有多个声明,但只有一个定义。这是怎么违反的?假设我有一个set<int>,我创建了一个函数,该函数将迭代器作为参数。由于它们的工作方式相同,因此我可以选择其类型:set<int>::iteratorset<int>::const_iterator。如果我听从建议,也就是说,我选择set<int>::iterator会怎样?

我写了一个程序来试图找到答案。基本上有 2 个函数,一个接受iterator,另一个接受const_iterator,我调用它们中的每一个两次,一次通过iterator,一次传递const_iterator。在这里:

#include <iostream>
#include <set>
void print_set_iter(std::set<int>::iterator& it) {
std::cout << "Set element from       iterator: " << *it       << "n";
}
void print_set_const_iter(std::set<int>::const_iterator& const_it) {
std::cout << "Set element from const_iterator: " << *const_it << "n";
}

int main() {
std::set<int> primes = {2, 3, 5, 7, 11};
std::set<int>::iterator             it = primes.find(3);
std::set<int>::const_iterator const_it = primes.find(5);
print_set_iter(it);
print_set_iter(const_it);
print_set_const_iter(it);
print_set_const_iter(const_it);
}

我已经使用 3 个最流行的编译器(gcc、clang、MSVC)在 rextester.com 上编译了这个:没有警告,它运行良好。通常我希望print_set_iter(const_it);会导致错误,但事实并非如此。这是否意味着这 3 个实现对两个迭代器使用相同的类型? 但即使在这种情况下,即使我发现编译器对这些迭代器不使用相同的类型,我仍然不明白为什么会有 ODR 违规。如果类型不同,则禁止的转换(从 const 到 non-const)应该会触发错误,但这与 ODR 无关。谁能给我举个违规的例子,或者解释一下那条说明是什么意思?

您必须以相同的方式命名函数才能获得错误。更改的代码:https://rextester.com/SSNZ54459

错误是

source_file.cpp: In function ‘void print_set_iter(std::set<int>::const_iterator&)’:
source_file.cpp:8:6: error: redefinition of ‘void print_set_iter(std::set<int>::const_iterator&)’
void print_set_iter(std::set<int>::const_iterator& const_it) {
^
source_file.cpp:4:6: note: ‘void print_set_iter(std::set<int>::iterator&)’ previously defined here
void print_set_iter(std::set<int>::iterator& it) {
^

有两种可能

类型
  • 别名可能引用也可能不引用同一类型;
  • ODR 违规不需要诊断,因此您无法通过(甚至大量)示例来反驳它的存在。

如果别名类型相同,则属于 ODR 冲突,如下所示:

using type_1 = int;
using type_2 = int;
void func(type_1) {}
void func(type_2) {}

别名类型在签名中考虑,而不是可以为每种类型创建的任意数量的别名。上述两个定义的签名是相同的。

最新更新