Annex C (informative) Compatibility [diff]

C.1 C++ and ISO C++ 2017 [diff.cpp17]

This subclause lists the differences between C++ and ISO C++ 2017 (ISO/IEC 14882:2017, Programming Languages — C++), by the chapters of this document.

C.1.1 [lex]: lexical conventions [diff.cpp17.lex]

Affected subclauses: [lex.pptoken], [module.unit], [module.import], [cpp.pre], [cpp.module], and [cpp.import]
Change: New identifiers with special meaning.

Rationale: Required for new features.

Effect on original feature: Logical lines beginning with module or import may be interpreted differently in this International Standard.
Example
:
class module {};
module m1;          // was variable declaration; now module-declaration
module *m2;         // variable declaration

class import {};
import j1;          // was variable declaration; now import-declaration
::import j2;        // variable declaration
— end example
 ]
Affected subclause: [lex.header]
Change: header-name tokens are formed in more contexts.

Rationale: Required for new features.

Effect on original feature: When the identifier import is followed by a < character, a header-name token may be formed.
Example
:
template<typename> class import {};
import<int> f();                // ill-formed; previously well-formed
::import<int> g();              // OK
— end example
 ]
Affected subclause: [lex.key]
Change: New keywords.

Rationale: Required for new features.
Effect on original feature: Valid C++ 2017 code using char8_­t, concept, consteval, constinit, co_­await, co_­yield, co_­return, or requires as an identifier is not valid in this International Standard.
Affected subclause: [lex.operators]
Change: New operator <=>.

Rationale: Necessary for new functionality.

Effect on original feature: Valid C++ 2017 code that contains a <= token immediately followed by a > token may be ill-formed or have different semantics in this International Standard:
namespace N {
  struct X {};
  bool operator<=(X, X);
  template<bool(X, X)> struct Y {};
  Y<operator<=> y;              // ill-formed; previously well-formed
}
Affected subclause: [lex.literal]
Change: Type of UTF-8 string and character literals.

Rationale: Required for new features.
The changed types enable function overloading, template specialization, and type deduction to distinguish ordinary and UTF-8 string and character literals.

Effect on original feature: Valid C++ 2017 code that depends on UTF-8 string literals having type “array of const char” and UTF-8 character literals having type “char” is not valid in this International Standard.
const auto *u8s = u8"text";     // u8s previously deduced as const char*; now deduced as const char8_­t*
const char *ps = u8s;           // ill-formed; previously well-formed

auto u8c = u8'c';               // u8c previously deduced as char; now deduced as char8_­t
char *pc = &u8c;                // ill-formed; previously well-formed

std::string s = u8"text";       // ill-formed; previously well-formed

void f(const char *s);
f(u8"text");                    // ill-formed; previously well-formed

template<typename> struct ct;
template<> struct ct<char> {
  using type = char;
};
ct<decltype(u8'c')>::type x;    // ill-formed; previously well-formed.

C.1.2 [basic]: basics [diff.cpp17.basic]

Affected subclause: [basic.life]
Change: A pseudo-destructor call ends the lifetime of the object to which it is applied.

Rationale: Increase consistency of the language model.

Effect on original feature: Valid ISO C++ 2017 code may be ill-formed or have undefined behavior in this International Standard.
Example
:
int f() {
  int a = 123;
  using T = int;
  a.~T();
  return a;         // undefined behavior; previously returned 123
}
— end example
 ]
Affected subclause: [intro.races]
Change: Except for the initial release operation, a release sequence consists solely of atomic read-modify-write operations.

Rationale: Removal of rarely used and confusing feature.

Effect on original feature: If a memory_­order_­release atomic store is followed by a memory_­order_­relaxed store to the same variable by the same thread, then reading the latter value with a memory_­order_­acquire load no longer provides any “happens before” guarantees, even in the absence of intervening stores by another thread.

C.1.3 [expr]: expressions [diff.cpp17.expr]

Affected subclause: [expr.prim.lambda.capture]
Change: Implicit lambda capture may capture additional entities.

Rationale: Rule simplification, necessary to resolve interactions with constexpr if.

Effect on original feature: Lambdas with a capture-default may capture local entities that were not captured in C++ 2017 if those entities are only referenced in contexts that do not result in an odr-use.

C.1.4 [dcl.dcl]: declarations [diff.cpp17.dcl.dcl]

Affected subclause: [dcl.typedef]
Change: Unnamed classes with a typedef name for linkage purposes can contain only C-compatible constructs.

Rationale: Necessary for implementability.

Effect on original feature: Valid C++ 2017 code may be ill-formed in this International Standard.
typedef struct {
  void f() {}           // ill-formed; previously well-formed
} S;
Affected subclause: [dcl.fct.default]
Change: A function cannot have different default arguments in different translation units.

Rationale: Required for modules support.

Effect on original feature: Valid C++ 2017 code may be ill-formed in this International Standard, with no diagnostic required.
// Translation unit 1
int f(int a = 42);
int g() { return f(); }

// Translation unit 2
int f(int a = 76) { return a; }         // ill-formed, no diagnostic required; previously well-formed
int g();
int main() { return g(); }              // used to return 42
Affected subclause: [dcl.init.aggr]
Change: A class that has user-declared constructors is never an aggregate.

Rationale: Remove potentially error-prone aggregate initialization which may apply notwithstanding the declared constructors of a class.

Effect on original feature: Valid C++ 2017 code that aggregate-initializes a type with a user-declared constructor may be ill-formed or have different semantics in this International Standard.
struct A {              // not an aggregate; previously an aggregate
  A() = delete;
};

struct B {              // not an aggregate; previously an aggregate
  B() = default;
  int i = 0;
};

struct C {              // not an aggregate; previously an aggregate
  C(C&&) = default;
  int a, b;
};

A a{};                  // ill-formed; previously well-formed
B b = {1};              // ill-formed; previously well-formed
auto* c = new C{2, 3};  // ill-formed; previously well-formed

struct Y;

struct X {
  operator Y();
};

struct Y {              // not an aggregate; previously an aggregate
  Y(const Y&) = default;
  X x;
};

Y y{X{}};               // copy constructor call; previously aggregate-initialization
Affected subclause: [dcl.init.list]
Change: Boolean conversion from a pointer or pointer-to-member type is now a narrowing conversion.

Rationale: Catches bugs.

Effect on original feature: Valid C++ 2017 code may fail to compile in this International Standard.
For example:
bool y[] = { "bc" };    // ill-formed; previously well-formed

C.1.5 [class]: classes [diff.cpp17.class]

Affected subclauses: [class.ctor] and [class.conv.fct]
Change: The class name can no longer be used parenthesized immediately after an explicit decl-specifier in a constructor declaration.
The conversion-function-id can no longer be used parenthesized immediately after an explicit decl-specifier in a conversion function declaration.

Rationale: Necessary for new functionality.

Effect on original feature: Valid C++ 2017 code may fail to compile in this International Standard.
For example:
struct S {
  explicit (S)(const S&);       // ill-formed; previously well-formed
  explicit (operator int)();    // ill-formed; previously well-formed
  explicit(true) (S)(int);      // OK
};
Affected subclauses: [class.ctor] and [class.dtor]
Change: A simple-template-id is no longer valid as the declarator-id of a constructor or destructor.

Rationale: Remove potentially error-prone option for redundancy.

Effect on original feature: Valid C++ 2017 code may fail to compile in this International Standard.
For example:
template<class T>
struct A {
  A<T>();           // error: simple-template-id not allowed for constructor
  A(int);           // OK, injected-class-name used
  ~A<T>();          // error: simple-template-id not allowed for destructor
};
Affected subclause: [class.copy.elision]
Change: A function returning an implicitly movable entity may invoke a constructor taking an rvalue reference to a type different from that of the returned expression.
Function and catch-clause parameters can be thrown using move constructors.

Rationale: Side effect of making it easier to write more efficient code that takes advantage of moves.

Effect on original feature: Valid C++ 2017 code may fail to compile or have different semantics in this International Standard.
For example:
struct base {
  base();
  base(base const &);
private:
  base(base &&);
};

struct derived : base {};

base f(base b) {
  throw b;                      // error: base(base &&) is private
  derived d;
  return d;                     // error: base(base &&) is private
}

struct S {
  S(const char *s) : m(s) { }
  S(const S&) = default;
  S(S&& other) : m(other.m) { other.m = nullptr; }
  const char * m;
};

S consume(S&& s) { return s; }

void g() {
  S s("text");
  consume(static_cast<S&&>(s));
  char c = *s.m;                // undefined behavior; previously ok
}

C.1.6 [over]: overloading [diff.cpp17.over]

Affected subclause: [over.match.oper]
Change: Equality and inequality expressions can now find reversed and rewritten candidates.

Rationale: Improve consistency of equality with three-way comparison and make it easier to write the full complement of equality operations.

Effect on original feature: Equality and inequality expressions between two objects of different types, where one is convertible to the other, could invoke a different operator.
Equality and inequality expressions between two objects of the same type could become ambiguous.
struct A {
  operator int() const;
};

bool operator==(A, int);        // #1
// #2 is built-in candidate: bool operator==(int, int);
// #3 is built-in candidate: bool operator!=(int, int);

int check(A x, A y) {
  return (x == y) +             // ill-formed; previously well-formed
    (10 == x) +                 // calls #1, previously selected #2
    (10 != x);                  // calls #1, previously selected #3
}

C.1.7 [temp]: templates [diff.cpp17.temp]

Affected subclause: [temp.names]
Change: An unqualified-id that is followed by a < and for which name lookup finds nothing or finds a function will be treated as a template-name in order to potentially cause argument dependent lookup to be performed.

Rationale: It was problematic to call a function template with an explicit template argument list via argument dependent lookup because of the need to have a template with the same name visible via normal lookup.

Effect on original feature: Previously valid code that uses a function name as the left operand of a < operator would become ill-formed.
struct A {};
bool operator<(void (*fp)(), A);
void f() {}
int main() {
  A a;
  f < a;    // ill-formed; previously well-formed
  (f) < a;  // still well formed
}

C.1.8 [except]: exception handling [diff.cpp17.except]

Affected subclause: [except.spec]
Change: Remove throw() exception specification.

Rationale: Removal of obsolete feature that has been replaced by noexcept.

Effect on original feature: A valid C++ 2017 function declaration, member function declaration, function pointer declaration, or function reference declaration that uses throw() for its exception specification will be rejected as ill-formed in this International Standard.
It should simply be replaced with noexcept for no change of meaning since C++ 2017.
Note
:
There is no way to write a function declaration that is non-throwing in this International Standard and is also non-throwing in C++ 2003 except by using the preprocessor to generate a different token sequence in each case.
— end note
 ]

C.1.9 [library]: library introduction [diff.cpp17.library]

Affected subclause: [headers]
Change: New headers.

Rationale: New functionality.

Effect on original feature: The following C++ headers are new: <barrier> ([barrier.syn]), <bit> ([bit.syn]), <charconv> ([charconv.syn]), <compare> ([compare.syn]), <concepts> ([concepts.syn]), <coroutine> ([coroutine.syn]), <format> ([format.syn]), <latch> ([latch.syn]), <numbers> ([numbers.syn]), <ranges> ([ranges.syn]), <semaphore> ([semaphore.syn]), <source_­location> ([source.location.syn]), <span> ([span.syn]), <stop_­token> ([thread.stoptoken.syn]), <syncstream> ([syncstream.syn]), and <version> ([support.limits.general]).
Valid C++ 2017 code that #includes headers with these names may be invalid in this International Standard.
Affected subclause: [headers]
Change: Remove vacuous C++ header files.

Rationale: The empty headers implied a false requirement to achieve C compatibility with the C++ headers.

Effect on original feature: A valid C++ 2017 program that #includes any of the following headers may fail to compile: <ccomplex>, <ciso646>, <cstdalign>, <cstdbool>, and <ctgmath>.
To retain the same behavior:
  • a #include of <ccomplex> can be replaced by a #include of <complex> ([complex.syn]),
  • a #include of <ctgmath> can be replaced by a #include of <cmath> ([cmath.syn]) and a #include of <complex>, and
  • a #include of <ciso646>, <cstdalign>, or <cstdbool> can simply be removed.

C.1.10 [containers]: containers library [diff.cpp17.containers]

Affected subclauses: [forwardlist] and [list]
Change: Return types of remove, remove_­if, and unique changed from void to container​::​size_­type.

Rationale: Improve efficiency and convenience of finding number of removed elements.

Effect on original feature: Code that depends on the return types might have different semantics in this International Standard.
Translation units compiled against this version of C++ may be incompatible with translation units compiled against C++ 2017, either failing to link or having undefined behavior.

C.1.11 [iterators]: iterators library [diff.cpp17.iterators]

Affected subclause: [iterator.traits]
Change: The specialization of iterator_­traits for void* and for function pointer types no longer contains any nested typedefs.

Rationale: Corrects an issue misidentifying pointer types that are not incrementable as iterator types.

Effect on original feature: A valid C++ 2017 program that relies on the presence of the typedefs may fail to compile, or have different behavior.

C.1.12 [algorithms]: algorithms library [diff.cpp17.alg.reqs]

Affected subclause: [algorithms.requirements]
Change: The number and order of deducible template parameters for algorithm declarations is now unspecified, instead of being as-declared.

Rationale: Increase implementor freedom and allow some function templates to be implemented as function objects with templated call operators.

Effect on original feature: A valid C++ 2017 program that passes explicit template arguments to algorithms not explicitly specified to allow such in this version of C++ may fail to compile or have undefined behavior.

C.1.13 [input.output]: input/output library [diff.cpp17.input.output]

Affected subclause: [istream.extractors]
Change: Character array extraction only takes array types.

Rationale: Increase safety via preventing buffer overflow at compile time.

Effect on original feature: Valid C++ 2017 code may fail to compile in this International Standard:
auto p = new char[100];
char q[100];
std::cin >> std::setw(20) >> p;         // ill-formed; previously well-formed
std::cin >> std::setw(20) >> q;         // OK
Affected subclause: [ostream.inserters.character]
Change: Overload resolution for ostream inserters used with UTF-8 literals.

Rationale: Required for new features.

Effect on original feature: Valid C++ 2017 code that passes UTF-8 literals to basic_­ostream<char, ...>​::​operator<< or basic_­ostream<wchar_­t, ...>​::​operator<< is now ill-formed.
std::cout << u8"text";          // previously called operator<<(const char*) and printed a string;
                                // now ill-formed
std::cout << u8'X';             // previously called operator<<(char) and printed a character;
                                // now ill-formed
Affected subclause: [ostream.inserters.character]
Change: Overload resolution for ostream inserters used with wchar_­t, char16_­t, or char32_­t types.

Rationale: Removal of surprising behavior.

Effect on original feature: Valid C++ 2017 code that passes wchar_­t, char16_­t, or char32_­t characters or strings to basic_­ostream<char, ...>​::​operator<< or that passes char16_­t or char32_­t characters or strings to basic_­ostream<wchar_­t, ...>​::​operator<< is now ill-formed.
std::cout << u"text";           // previously formatted the string as a pointer value;
                                // now ill-formed
std::cout << u'X';              // previously formatted the character as an integer value;
                                // now ill-formed
Affected subclause: [fs.class.path]
Change: Return type of filesystem path format observer member functions.

Rationale: Required for new features.

Effect on original feature: Valid C++ 2017 code that depends on the u8string() and generic_­u8string() member functions of std​::​filesystem​::​path returning std​::​string is not valid in this International Standard.
std::filesystem::path p;
std::string s1 = p.u8string();          // ill-formed; previously well-formed
std::string s2 = p.generic_u8string();  // ill-formed; previously well-formed

C.1.14 [depr]: compatibility features [diff.cpp17.depr]

Change: Remove uncaught_­exception.

Rationale: The function did not have a clear specification when multiple exceptions were active, and has been superseded by uncaught_­exceptions.

Effect on original feature: A valid C++ 2017 program that calls std​::​uncaught_­exception may fail to compile.
It might be revised to use std​::​uncaught_­exceptions instead, for clear and portable semantics.
Change: Remove support for adaptable function API.
Rationale: The deprecated support relied on a limited convention that could not be extended to support the general case or new language features.
It has been superseded by direct language support with decltype, and by the std​::​bind and std​::​not_­fn function templates.

Effect on original feature: A valid C++ 2017 program that relies on the presence of result_­type, argument_­type, first_­argument_­type, or second_­argument_­type in a standard library class may fail to compile.
A valid C++ 2017 program that calls not1 or not2, or uses the class templates unary_­negate or binary_­negate, may fail to compile.
Change: Remove redundant members from std​::​allocator.

Rationale: std​::​allocator was overspecified, encouraging direct usage in user containers rather than relying on std​::​allocator_­traits, leading to poor containers.

Effect on original feature: A valid C++ 2017 program that directly makes use of the pointer, const_­pointer, reference, const_­reference, rebind, address, construct, destroy, or max_­size members of std​::​allocator, or that directly calls allocate with an additional hint argument, may fail to compile.
Change: Remove raw_­storage_­iterator.

Rationale: The iterator encouraged use of algorithms that might throw exceptions, but did not return the number of elements successfully constructed that might need to be destroyed in order to avoid leaks.

Effect on original feature: A valid C++ 2017 program that uses this iterator class may fail to compile.
Change: Remove temporary buffers API.
Rationale: The temporary buffer facility was intended to provide an efficient optimization for small memory requests, but there is little evidence this was achieved in practice, while requiring the user to provide their own exception-safe wrappers to guard use of the facility in many cases.

Effect on original feature: A valid C++ 2017 program that calls get_­temporary_­buffer or return_­temporary_­buffer may fail to compile.
Change: Remove shared_­ptr​::​unique.

Rationale: The result of a call to this member function is not reliable in the presence of multiple threads and weak pointers.
The member function use_­count is similarly unreliable, but has a clearer contract in such cases, and remains available for well defined use in single-threaded cases.

Effect on original feature: A valid C++ 2017 program that calls unique on a shared_­ptr object may fail to compile.
Affected subclause: [depr.meta.types]
Change: Remove deprecated type traits.

Rationale: The traits had unreliable or awkward interfaces.
The is_­literal_­type trait provided no way to detect which subset of constructors and member functions of a type were declared constexpr.
The result_­of trait had a surprising syntax that could not report the result of a regular function type.
It has been superseded by the invoke_­result trait.

Effect on original feature: A valid C++ 2017 program that relies on the is_­literal_­type or result_­of type traits, on the is_­literal_­type_­v variable template, or on the result_­of_­t alias template may fail to compile.