Why does passing to a function a set::iterator instead of a const_iterator violate the One Definition Rule?
Asked Answered
O

2

10

The description of the std::set container given by cppreference.com contains this note at the end:

The member types iterator and const_iterator may be aliases to the same type. Since iterator is convertible to const_iterator, const_iterator should be used in function parameter lists to avoid violations of the One Definition Rule.

I don't understand this last remark. What I understand is that a set doesn't allow modifying its elements (if you need to change one, you have to erase it and then insert the new one), so every iterator works as a const_iterator. The standard adds that it is possible (but not required) that they are the same type. So far it's clear.

What I don't get is the possible violation of the One Definition Rule. That rule says that a function can have many declarations, but only one definition. How is that violated? Let's say I have a set<int>, and I create a function which takes as argument an iterator. Since they work the same way, I can choose its type: either set<int>::iterator or set<int>::const_iterator. What happens if I do not follow the advice, that is, I choose set<int>::iterator?

I've written a program to try to find an answer. Basically there are 2 functions, one accepting an iterator and the other accepting a const_iterator, and I call each of them twice, once passing an iterator and once passing a const_iterator. Here it is:

#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);
}

I've compiled this on rextester.com using the 3 most popular compilers (gcc, clang, MSVC): there are no warnings, and it runs fine. Normally I'd expect print_set_iter(const_it); to cause an error, but it doesn't. Does it mean that these 3 implementations are using the same type for both iterators? But even in that case, and even if I found a compiler that doesn't use the same type for these iterators, I still don't understand why there would be an ODR violation. If the types were different, the forbidden conversion (from const to non-const) should trigger an error, but that has nothing to do with ODR. Can anyone show me an example of that violation, or explain what that note means?

Obscurant answered 21/6, 2019 at 9:59 Comment(3)
FWIW, it's Template:cpp/container/assoc_note.Discreditable
I guess I don't currently have permission to edit cppreference.com, but I put in a change suggestion: en.cppreference.com/w/Talk:Main_Page/…Yellowbird
@Yellowbird Great, I didn't know it is possible to suggest edits to cppreference.com! I'm still working on this issue (well, I got interrupted today...) but I can say the answers here have helped me clarifying the situation, and I've realized I was on a completely wrong track, and at least in part I blame the way they phrased it. Thanks for this, I really appreciate it!Obscurant
R
8

There are two maybes:

If the aliased types are the same, then it is an ODR violation, same as this:

using type_1 = int;
using type_2 = int;

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

The aliased types are considered in the signature, not an arbitrary number of aliases that can be created for each type. The signature of the two definitions above is the same.

Retinite answered 21/6, 2019 at 10:9 Comment(0)
G
8

you must name the functions the same way to get the error. Changed code: https://rextester.com/SSNZ54459

And the error is

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) {
      ^
Ginn answered 21/6, 2019 at 10:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.