> 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@kostur.net>:
 
On Mon, Apr 6, 2026 at 1:41 PM Рябиков Александр <rsashka@mail.ru> 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.