Date: Thu, 12 Jun 2025 16:49:16 +0200
On Thu, 2025-06-12 at 13:36 +0100, Jennifier Burnett wrote:
> > What I'm reading into this is that it is UB if a standard library
> > function calls a const member function of an object that was passed
> > to
> > it via a const ref, and the the const member function "modifies"
> > the
> > object (via const_cast or a mutable member).
> > However, I doubt that this is the correct interpretation.
>
> I believe the clause is saying is that any object modified by the
> standard library function must be derivable from it's non-const
> arguments including the implicit this pointer (i.e. not derived from
> a global), not that any const object derivable from the arguments
> won't be modified.
>
> So it would be UB by the quoted clause for a standard library
> function to for example:
>
> ```
> int* global;
>
> int foo()
> {
> int a, b;
> b = 2;
> global = &b;
> std::bar(&a);
> return b + a;
> }
> void std::bar(int* ref)
> {
> *global = 0;
> *ref = 5;
> }
> ```
>
> Because the access through `global` is not done indirectly through
> the function's non-const arguments.
>
Isn't that already covered in Paragraph 2? It reads:
"A C++ standard library function shall not directly or indirectly
access objects ([intro.multithread]) accessible by threads other than
the current thread unless the objects are accessed directly or
indirectly via the function's arguments, including this."
> If you had a function like this, however:
>
> ```
> void std::foo(const int* ref)
> {
> *const_cast<int*>(ref) = 42;
> }
> ```
>
> That's fine by that clause, because the pointer argument itself is
> non-const (just the object pointed to by it). If the object pointed
> to by ref was declared as const that would be UB, but by a general
> language rule and not by the clause you quote. So long as the object
> pointed to by ref is ultimately mutable the clause seems to be fine
> with it. In order to trigger the clause you'd need the signature to
> be:
>
> ```
> void std::foo(const int* const ref)
> ```
>
> Because the access would be derived from a const argument. I'm not
> sure that's the exact intention of the clause but that's what a
> strict reading gives me.
I agree with you that a `const int* p` is not technically a const
argument (would a reference be a const argument?). Assuming your above
definition of `std::foo` was legal, I am even more confused how Herb
Sutter derives from the cited rule (it didn't change substantially
since C++11) that const implies thread-safe for all operations called
on an object by the standard library.
Best
Marvin
>
> On 12 June 2025 09:56:16 BST, Marvin Williams via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> > Hi all,
> >
> > in the current working draft, clause [res.on.data.races]
> > (ยง16.4.6.10,
> > https://eel.is/c++draft/res.on.data.races), which regards data race
> > avoidance in the standard library implementation, states (Paragraph
> > 3):
> >
> > "A C++ standard library function shall not directly or indirectly
> > modify objects ([intro.multithread]) accessible by threads other
> > than
> > the current thread unless the objects are accessed directly or
> > indirectly via the function's non-const arguments, including this."
> >
> > What I'm reading into this is that it is UB if a standard library
> > function calls a const member function of an object that was passed
> > to
> > it via a const ref, and the the const member function "modifies"
> > the
> > object (via const_cast or a mutable member).
> > However, I doubt that this is the correct interpretation.
> >
> > There is some discussion about this clause, but I couldn't find a
> > definitive answer.
> > Most notably, there is a talk by Herb Sutter:
> > https://web.archive.org/web/20170119232617/https://channel9.msdn.com/posts/C-and-Beyond-2012-Herb-Sutter-You-dont-know-blank-and-blank
> >
> > At about 15:35, he states that since C++11, const implies
> > threadsafe,
> > because it implies either bitwise const or internal synchronization
> > (e.g., by a mutable std::mutex).
> >
> > With the above interpretation, I can see how const may imply
> > bitwise
> > const, but I fail to see how Herb derives the internal
> > synchronization
> > requirement. In my understanding, the example given at 26:00,
> > locking a
> > mutable std::mutex member still modifies the object, violating
> > [res.on.data.races], if a standard library function were to call
> > w.get_info() on a const widget& w.
> >
> > My main questions are:
> > What are the requirements for const member functions that are
> > intended
> > to be used with the standard library, and how does thread-safety
> > relate
> > to the const qualifier?
> >
> > Best
> > Marvin
> > --
> > Std-Discussion mailing list
> > Std-Discussion_at_[hidden]
> > https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
> > What I'm reading into this is that it is UB if a standard library
> > function calls a const member function of an object that was passed
> > to
> > it via a const ref, and the the const member function "modifies"
> > the
> > object (via const_cast or a mutable member).
> > However, I doubt that this is the correct interpretation.
>
> I believe the clause is saying is that any object modified by the
> standard library function must be derivable from it's non-const
> arguments including the implicit this pointer (i.e. not derived from
> a global), not that any const object derivable from the arguments
> won't be modified.
>
> So it would be UB by the quoted clause for a standard library
> function to for example:
>
> ```
> int* global;
>
> int foo()
> {
> int a, b;
> b = 2;
> global = &b;
> std::bar(&a);
> return b + a;
> }
> void std::bar(int* ref)
> {
> *global = 0;
> *ref = 5;
> }
> ```
>
> Because the access through `global` is not done indirectly through
> the function's non-const arguments.
>
Isn't that already covered in Paragraph 2? It reads:
"A C++ standard library function shall not directly or indirectly
access objects ([intro.multithread]) accessible by threads other than
the current thread unless the objects are accessed directly or
indirectly via the function's arguments, including this."
> If you had a function like this, however:
>
> ```
> void std::foo(const int* ref)
> {
> *const_cast<int*>(ref) = 42;
> }
> ```
>
> That's fine by that clause, because the pointer argument itself is
> non-const (just the object pointed to by it). If the object pointed
> to by ref was declared as const that would be UB, but by a general
> language rule and not by the clause you quote. So long as the object
> pointed to by ref is ultimately mutable the clause seems to be fine
> with it. In order to trigger the clause you'd need the signature to
> be:
>
> ```
> void std::foo(const int* const ref)
> ```
>
> Because the access would be derived from a const argument. I'm not
> sure that's the exact intention of the clause but that's what a
> strict reading gives me.
I agree with you that a `const int* p` is not technically a const
argument (would a reference be a const argument?). Assuming your above
definition of `std::foo` was legal, I am even more confused how Herb
Sutter derives from the cited rule (it didn't change substantially
since C++11) that const implies thread-safe for all operations called
on an object by the standard library.
Best
Marvin
>
> On 12 June 2025 09:56:16 BST, Marvin Williams via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> > Hi all,
> >
> > in the current working draft, clause [res.on.data.races]
> > (ยง16.4.6.10,
> > https://eel.is/c++draft/res.on.data.races), which regards data race
> > avoidance in the standard library implementation, states (Paragraph
> > 3):
> >
> > "A C++ standard library function shall not directly or indirectly
> > modify objects ([intro.multithread]) accessible by threads other
> > than
> > the current thread unless the objects are accessed directly or
> > indirectly via the function's non-const arguments, including this."
> >
> > What I'm reading into this is that it is UB if a standard library
> > function calls a const member function of an object that was passed
> > to
> > it via a const ref, and the the const member function "modifies"
> > the
> > object (via const_cast or a mutable member).
> > However, I doubt that this is the correct interpretation.
> >
> > There is some discussion about this clause, but I couldn't find a
> > definitive answer.
> > Most notably, there is a talk by Herb Sutter:
> > https://web.archive.org/web/20170119232617/https://channel9.msdn.com/posts/C-and-Beyond-2012-Herb-Sutter-You-dont-know-blank-and-blank
> >
> > At about 15:35, he states that since C++11, const implies
> > threadsafe,
> > because it implies either bitwise const or internal synchronization
> > (e.g., by a mutable std::mutex).
> >
> > With the above interpretation, I can see how const may imply
> > bitwise
> > const, but I fail to see how Herb derives the internal
> > synchronization
> > requirement. In my understanding, the example given at 26:00,
> > locking a
> > mutable std::mutex member still modifies the object, violating
> > [res.on.data.races], if a standard library function were to call
> > w.get_info() on a const widget& w.
> >
> > My main questions are:
> > What are the requirements for const member functions that are
> > intended
> > to be used with the standard library, and how does thread-safety
> > relate
> > to the const qualifier?
> >
> > Best
> > Marvin
> > --
> > Std-Discussion mailing list
> > Std-Discussion_at_[hidden]
> > https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
Received on 2025-06-12 14:49:22