GCC 10 adds C++20 facilities with distinct implementation boundaries
GCC 10 implements concepts, experimental coroutines, and constexpr allocation. Each facility changes a different part of how a program states requirements and manages lifetime.
A language revision can give a compiler a common target without making every new facility behave like one indivisible switch. The GCC 10.1 announcement on May 7 describes progress on C++20 in both the compiler and library. Concepts and coroutines are among the language additions, while constant evaluation gains support for allocation. Their practical boundaries differ.
The GCC 10.1 dialect manual makes one boundary explicit. Concepts belong to the C++2a mode, while coroutines use the separate experimental -fcoroutines option. A build needs to request the facilities it uses instead of assuming that the release number alone describes its language configuration.
C++'s ability to move abstraction costs out of runtime execution is useful to me. The source still needs an understandable account of requirements and lifetime. These additions deserve separate explanations because each changes a different part of that account.
A template can state its admission requirements
Concepts let a generic declaration express requirements on its arguments. A template body can otherwise rely on operations that the public declaration does not reveal. The caller discovers the missing operation through instantiation, potentially after following several implementation helpers.
A named constraint moves the relevant syntactic requirement toward the interface. The compiler can consider it when determining whether the declaration applies. This makes an unsuitable argument a question about the public contract, rather than only a failure somewhere in the implementation.
The GCC manual distinguishes standard concepts from its earlier Concepts TS support. That matters for code written against an experimental facility. A familiar option name or a familiar keyword does not establish that earlier source has the same meaning under the standard direction.
For an application library, the useful task is to describe the actual requirement. If a function needs incrementing, dereferencing alone is insufficient. If it needs a semantic relationship between operations, merely making each expression well formed does not establish that relationship.
A concept name therefore remains part of the author's documentation. The compiler checks what the definition requests, while the interface explains why those requests make the operation suitable. Clearer constraints can improve the account of a template without removing the need for a semantic contract.
A coroutine is a mechanism for suspended work
The coroutine merge paper P0912R5 brings the coroutine machinery into the C++20 working draft. GCC 10 supplies experimental support through its dedicated option. A build using it can request that support explicitly.
g++ -std=c++2a -fcoroutines -c worker.cpp
This command states which compiler facilities the source expects. It does not supply a coroutine return type or an asynchronous library. Those remain part of the source and its dependencies.
The distinction matters because suspension changes the duration of work. A function may retain state while another operation proceeds, then resume later. The application needs an owner for that retained work and a rule for its eventual completion or destruction.
A short co_await expression cannot describe the whole contract by itself. The types involved determine what awaiting means for the operation. A library must explain how the caller obtains a result and what obligations remain while the computation is suspended.
This also affects resource reasoning. A local resource can remain live through suspension, so its lifetime is longer than a quick reading of the surrounding statements may suggest. The relevant boundary is the lifetime of the suspended computation, including the operation that ultimately destroys it.
RAII remains valuable here, but the owner of the computation must be explicit. Destruction can release resources only when something causes destruction at the correct time. A result wrapper with an unclear ownership rule would make the language machinery harder to use safely.
Constant evaluation gains temporary dynamic storage
GCC 10's C++20 work also includes constexpr allocation. The underlying P0784R7 proposal explains the important restriction. Transient allocations occur during constant evaluation and are released before that evaluation ends. The proposal removed its earlier non-transient allocation design.
That makes the feature more specific than creating arbitrary persistent heap objects during compilation. A calculation can use temporary dynamic storage while producing a result whose lifetime does not depend on retaining that storage.
A small language example demonstrates the distinction.
constexpr int derive_value()
{
int* scratch = new int(7);
int result = *scratch + 1;
delete scratch;
return result;
}
static_assert(derive_value() == 8);
The temporary allocation is part of the calculation. The returned integer does not point into it, and the function releases the allocation before the evaluation completes. That matches the transient model.
The design gives constant-evaluated algorithms a way to use storage whose needs arise during the calculation. It does not require the programmer to know every intermediate capacity before beginning the work. The result still needs a representation compatible with the surrounding constant-expression requirements.
This is relevant to algorithms that discover how much intermediate state they need as they proceed. A fixed-capacity substitute can force a separate sizing pass or impose an arbitrary upper bound. Dynamic scratch storage can make the source closer to the algorithm's actual needs.
The feature nevertheless separates temporary working memory from the final result. Returning a pointer into released scratch storage would not create a useful persistent object. The program's output needs to survive independently of the allocations whose purpose was only to compute it.
Language support does not imply every container is ready
The allocation facility is part of the compiler's constant-evaluation rules. A container used inside such a calculation also needs suitable library operations. The compiler accepting a simple new and delete example does not establish that every container operation is constant-evaluable.
This is a recurring distinction in a compiler release. A language mechanism enables library work, while library declarations and implementations determine which public facilities can use it. An adoption claim needs to identify both sides when the source relies on both.
The same applies to concepts. A compiler can accept a user-defined concept without a particular standard-library concept being available in the selected library. Keeping the example's requirement in the source avoids silently depending on a different component's implementation status.
For coroutines, the corresponding missing layer can be an application-level abstraction. The compiler provides the protocol machinery, but the program still needs types that implement a meaningful task or sequence. That is a different requirement from a missing keyword.
The useful comparison is between contracts
These facilities reduce different kinds of incidental work. A constraint can state what the generic function requires. Coroutine machinery can represent suspended control flow. Transient allocation lets a constant-evaluated calculation manage intermediate storage. Their source-level effects are related through the need to describe valid use.
A library adopting them should therefore explain the new contract in terms its callers need. A caller of a constrained function needs to understand what makes an argument suitable. Holding a coroutine result requires the caller to understand ownership. A consumer of a derived constant needs a result that does not depend on expired temporary storage.
I am more interested in those concrete improvements than in increasing the amount of new syntax a codebase contains. A feature is useful when it removes an unclear convention or gives an existing requirement a better representation. GCC 10 makes several such representations available, with explicit boundaries around their support.
Those boundaries also keep compatibility discussions specific. A build can name its language mode and experimental options. A library can state which declarations it requires. The programmer can then distinguish a compiler-language issue from a library dependency or an application ownership mistake.
An interface can also hide the mechanism without hiding its obligations. A public function might return an ordinary value derived through constant evaluation, leaving consumers independent of its scratch-storage strategy. Another interface might deliberately expose a constrained template because its generic behavior is part of the API. The right boundary depends on which decisions callers must make. This is one reason a release can enable a useful implementation change without requiring every consumer to rewrite its source. New language machinery can remain inside the component that benefits from it, while the public contract continues to describe a stable result.
The GCC 10 release establishes implementation progress on each facility. Its practical value is that source can now state more of the requirements and lifetime decisions that previously relied on conventions or separate machinery. The program still needs to connect those decisions through an interface whose callers can understand.