ISO/IEC JTC1 SC22 WG21 N2514 = 08-0024 - 2008-02-01
Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org
The early proposals for atomics, culminating in N2324, provided for implicit conversions from an atomic type to the corresponding base type as a more syntactically concise method for specifying the load operation.
Because of the problems with implicit conversions, the library subcommittee directed their removal from the atomics proposal, which was reflected in N2324 and that paper's final version, N2427, and is reflected in the current working draft standard, N2461.
However, there is substantial support among members of the committee to reverse that decision and add implicit conversions back into atomics. This paper clarifies the issues and provides normative wording for such a change.
Within this discussion, implict conversion operators yielding the base type of an atomic type will be called implicit loads. Calling one of load functions will be called an explicit load.
The general use of implicit conversions
has caused significant ambiguities and insecurities in working code.
Such problems are described in
N1952
and its successors,
most recently
N2437.
However, a common feature of these problems
is that the conversion returns an attribute of the object.
As examples,
string_type::operator const char*
returns a representation address
and
smart_pointer::operator bool
returns the result of a test for null.
In contrast, the implicit load defined in N2324 returns an alternate representation of the atomic's abstract value. Indeed, as the atomic types have deleted copy constructors and no other operations returning atomics, there is no other representation of the abstract value of the atomic.
Furthermore, as the atomic types have deleted copy constructors, user-defined functions cannot have them as parameters or return types. Thus, there can be no additional potential ambiguity in calls to such functions with arguments formed from explicit loads.
In summary, atomic objects are only lvalues, never rvalues. So, while use of implicit conversion operators should usually be viewed with suspicion, the proposed use in atomics introduces no problems.
Implicit load makes the cost of an atomic load essentially invisible in the source. As atomic loads can be expensive, the invisibility of the cost can thus lead programmers to write code that is significantly more expensive than necessary.
The situation is similar for the assignment operator, which allows minimal, but not invisible, syntax for an atomic store operation.
Related to the hidden cost of implicit load, is the hidden semantic implications of reading an atomic variable more than once. Algorithms using atomic variables are much more likely to be correct if they read the variables only once. The additional syntactic burden of writing an explicit load both encourages minimizing the number of loads and makes multiple loads on the same variable more obvious.
The situation is less problematic for the assignment operator, as programmers are less likely to perform multiple assignments and as the operation has some explicit syntax.
In many contexts where code is presented, physical space is at a premium and non-essential details are unwelcome. In such contexts, presenters will be tempted to write implicit loads even if the language does not provide them. Such code will be syntactically incorrect, thus inhibiting compilation of examples and inhibiting the conversion of examples into working code.
Presentations, articles, and papers will likely assume or require sequentially-consistent operations, which are often stronger than algorithms require. The consequences are that presented algorithms are more understandable but less efficient. This tradeoff manifests itself in both published papers on lock-free algorithms and on the adoption of those algorithms in real-world environments.
This SC focus is already present in the overloaded assignment and increment operators for atomics. The concern is the additional degree of focus.
Other programming languages, notably Java and C#, have implicit load. As a result, C++ programs without implicit load will look clumsier than other languages.
The following changes add implicit load to the atomics library.
To struct atomic_bool
, add member:
operator bool() const volatile;
To struct atomic_itype
, add member:
operator integral() const volatile;
To struct atomic_address
, add member:
operator void*() const volatile;
To template struct atomic
, add member:
operator T() const volatile;
To template struct atomic<integral>
specializations, add member:
operator integral() const volatile;
To template struct atomic<T*>
specializations, add member:
operator T*() const volatile;
After paragraph 11, add the following specification.
A::operator C() const volatile;
Effects:
load()
Returns: The result of
load()
.