Date: Tue, 07 Apr 2026 11:04:14 +0300
> Still looking for concrete examples. And that sounds more like a
> restriction on the instance of a type, not something inherent to a
> type. But concrete examples may clarify this point.
> Still looking for concrete examples...
The most obvious example of such a type is the standard std::scoped_lock. Theoretically, any instance of this type should only be created in automatic objects, but the language currently doesn't prohibit its arbitrary use, and the restriction explicitly stated in the std:: *scoped* _lock type name itself is merely a gentleman's agreement, not guaranteed by the language.
Another concrete example is all classes derived from non-owning view types (span, string_view, and the like), which can also be restricted to instances of automatic storage duration objects.
> Not sure why you're adding the destruction clause in there. All
> local scope objects are guaranteed to be destroyed at the end of the
> scope.
Some types may have pointer-based implementations and methods in their API that allow pointers to leave the object instance. This way, the object instance will be deleted, but the data itself won't be destroyed. I think this is important, which is what I tried to describe in section "7. Ownership Contract."
> Does this mean that the function:
>
> void addThingsToContainer(std::vector<T>& vec) {
> vec.push_back(T{});
> }
A reference to an owner does not create a new lifetime root; it only provides borrowed access to the existing owner object. So in:
> void addThingsToContainer(std::vector<T>& vec) {
> vec.push_back(T{});
> }
the fact that `vec` is a reference should not by itself make the program ill-formed.
The diagnostic should instead happen where an invalid owner root is created, e.g.:
static std::vector<T> gv; // ill-formed
thread_local std::vector<T> tv; // ill-formed
auto* pv = new std::vector<T>; // ill-formed
void addThingsTwice() {
std::vector<T> v; // OK
addThingsToContainer(v);
addThingsToContainer(v);
addThingsToContainer(gv); // Correct call, but ill-formed diagnostics occur earlier.
addThingsToContainer(tv); // Correct call, but ill-formed diagnostics occur earlier.
addThingsToContainer(*pv); // Correct call, but ill-formed diagnostics occur earlier.
}
In other words, the restriction should apply to the lifetime root of the complete owner object, not to borrowed access paths such as references.
>
> Вторник, 7 апреля 2026, 00:35 +03:00 от Andre Kostur <andre_at_[hidden]>:
> On Mon, Apr 6, 2026 at 1:41 PM Рябиков Александр <rsashka_at_[hidden]> wrote:
> >
> > I am not claiming that this is a pervasive problem across all C++ code.
> However, it does arise for types whose meaning must be strictly confined
> to the current scope.
>
> Still curious for specific examples of such types that must have that
> constraint that cannot be violated for any reason.
>
> > Such use cases are primarily needed to implement safe memory management
> and deterministic concurrent programming.
>
> Still looking for concrete examples. And that sounds more like a
> restriction on the instance of a type, not something inherent to a
> type. But concrete examples may clarify this point.
>
> > The problem I am trying to address is that the language should be able
> to guarantee that "this type may exist only in local scope and is
> guaranteed to be destroyed when the current scope exits"
>
> Not sure why you're adding the destruction clause in there. All
> local scope objects are guaranteed to be destroyed at the end of the
> scope.
>
> > Without language-level (compiler-enforced) guarantees, it is not
> possible to implement the use cases described above reliably; at present,
> they can only be approximated through conventions, code review, or custom
> tooling.
>
> Still unsure whether this is something that we need to enshrine in the
> Standard and force every compiler to enforce.
>
>
> Does this mean that the function:
>
> void addThingsToContainer(std::vector<T> & vec) {
> vec.push_back(T{});
> }
>
> Should fail to compile because I'm adding it to a vector passed by
> reference? Even if the next function is:
>
> void addThingsTwice() {
> std::vector<T> v;
> addThingsToContainer(v);
> addThingsToContainer(v);
> }
>
> Note that the first one has external linkage, and thus may be called
> from a different translation unit.
>
> restriction on the instance of a type, not something inherent to a
> type. But concrete examples may clarify this point.
> Still looking for concrete examples...
The most obvious example of such a type is the standard std::scoped_lock. Theoretically, any instance of this type should only be created in automatic objects, but the language currently doesn't prohibit its arbitrary use, and the restriction explicitly stated in the std:: *scoped* _lock type name itself is merely a gentleman's agreement, not guaranteed by the language.
Another concrete example is all classes derived from non-owning view types (span, string_view, and the like), which can also be restricted to instances of automatic storage duration objects.
> Not sure why you're adding the destruction clause in there. All
> local scope objects are guaranteed to be destroyed at the end of the
> scope.
Some types may have pointer-based implementations and methods in their API that allow pointers to leave the object instance. This way, the object instance will be deleted, but the data itself won't be destroyed. I think this is important, which is what I tried to describe in section "7. Ownership Contract."
> Does this mean that the function:
>
> void addThingsToContainer(std::vector<T>& vec) {
> vec.push_back(T{});
> }
A reference to an owner does not create a new lifetime root; it only provides borrowed access to the existing owner object. So in:
> void addThingsToContainer(std::vector<T>& vec) {
> vec.push_back(T{});
> }
the fact that `vec` is a reference should not by itself make the program ill-formed.
The diagnostic should instead happen where an invalid owner root is created, e.g.:
static std::vector<T> gv; // ill-formed
thread_local std::vector<T> tv; // ill-formed
auto* pv = new std::vector<T>; // ill-formed
void addThingsTwice() {
std::vector<T> v; // OK
addThingsToContainer(v);
addThingsToContainer(v);
addThingsToContainer(gv); // Correct call, but ill-formed diagnostics occur earlier.
addThingsToContainer(tv); // Correct call, but ill-formed diagnostics occur earlier.
addThingsToContainer(*pv); // Correct call, but ill-formed diagnostics occur earlier.
}
In other words, the restriction should apply to the lifetime root of the complete owner object, not to borrowed access paths such as references.
>
> Вторник, 7 апреля 2026, 00:35 +03:00 от Andre Kostur <andre_at_[hidden]>:
> On Mon, Apr 6, 2026 at 1:41 PM Рябиков Александр <rsashka_at_[hidden]> wrote:
> >
> > I am not claiming that this is a pervasive problem across all C++ code.
> However, it does arise for types whose meaning must be strictly confined
> to the current scope.
>
> Still curious for specific examples of such types that must have that
> constraint that cannot be violated for any reason.
>
> > Such use cases are primarily needed to implement safe memory management
> and deterministic concurrent programming.
>
> Still looking for concrete examples. And that sounds more like a
> restriction on the instance of a type, not something inherent to a
> type. But concrete examples may clarify this point.
>
> > The problem I am trying to address is that the language should be able
> to guarantee that "this type may exist only in local scope and is
> guaranteed to be destroyed when the current scope exits"
>
> Not sure why you're adding the destruction clause in there. All
> local scope objects are guaranteed to be destroyed at the end of the
> scope.
>
> > Without language-level (compiler-enforced) guarantees, it is not
> possible to implement the use cases described above reliably; at present,
> they can only be approximated through conventions, code review, or custom
> tooling.
>
> Still unsure whether this is something that we need to enshrine in the
> Standard and force every compiler to enforce.
>
>
> Does this mean that the function:
>
> void addThingsToContainer(std::vector<T> & vec) {
> vec.push_back(T{});
> }
>
> Should fail to compile because I'm adding it to a vector passed by
> reference? Even if the next function is:
>
> void addThingsTwice() {
> std::vector<T> v;
> addThingsToContainer(v);
> addThingsToContainer(v);
> }
>
> Note that the first one has external linkage, and thus may be called
> from a different translation unit.
>
Received on 2026-04-07 08:04:21
