为什么将set :: iterator而不是const_iterator传递给函数会违反“一个定义规则”?

时间:2019-06-21 09:59:23

标签: c++ iterator set parameter-passing one-definition-rule

std::set容器given by cppreference.com的描述在结尾处包含以下注释:

  

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

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

我不明白是可能违反了One Definition Rule。该规则表示一个函数可以有许多声明,但只能有一个定义。如何违反?假设我有一个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);
}

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

2 个答案:

答案 0 :(得分:8)

有两个也许

如果别名类型相同,则表示违反了ODR,与此相同:

using type_1 = int;
using type_2 = int;

void func(type_1) {}
void func(type_2) {}

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

答案 1 :(得分:8)

您必须以相同的方式命名函数以获取错误。更改的代码: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) {
      ^