【问题标题】:Why does passing to a function a set::iterator instead of a const_iterator violate the One Definition Rule?为什么将 set::iterator 而不是 const_iterator 传递给函数违反了单一定义规则?
【发布时间】:2019-11-04 04:02:41
【问题描述】:

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); 会导致错误,但事实并非如此。这是否意味着这 3 个实现对两个迭代器使用相同的类型? 但即使在那种情况下,即使我找到了一个不为这些迭代器使用相同类型的编译器,我仍然不明白为什么会出现 ODR 违规。如果类型不同,则禁止转换(从 const 到非 const)应该会触发错误,但这与 ODR 无关。谁能给我看一个违规的例子,或者解释一下这个注释是什么意思?

【问题讨论】:

  • 我想我目前没有编辑 cppreference.com 的权限,但我提出了更改建议:en.cppreference.com/w/Talk:Main_Page/…
  • @aschepler 太好了,我不知道可以建议对 cppreference.com 进行编辑!我仍在研究这个问题(好吧,我今天被打断了......)但我可以说这里的答案帮助我澄清了情况,我意识到我走上了完全错误的轨道,至少在我责怪他们措辞的方式。谢谢你,我真的很感激!

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


【解决方案1】:

有两种可能

如果别名类型相同,则为 ODR 违规,如下:

using type_1 = int;
using type_2 = int;

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2017-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-31
      • 1970-01-01
      • 1970-01-01
      • 2020-09-07
      • 2021-12-08
      相关资源
      最近更新 更多