diff --git a/libcxx/docs/DesignDocs/ExtendedCXX03Support.rst b/libcxx/docs/DesignDocs/ExtendedCXX03Support.rst new file mode 100644 index 000000000000..e9e3fc4d2303 --- /dev/null +++ b/libcxx/docs/DesignDocs/ExtendedCXX03Support.rst @@ -0,0 +1,118 @@ +======================= +Extended C++03 Support +======================= + +.. contents:: + :local: + +Overview +======== + +libc++ is an implementation of the C++ standard library targeting C++11 or later. + +In C++03, the library implements the C++11 standard using C++11 language extensions provided +by Clang. + +This document tracks the C++11 extensions libc++ requires, the C++11 extensions it provides, +and how to write minimal C++11 inside libc++. + +Required C++11 Compiler Extensions +================================== + +Clang provides a large subset of C++11 in C++03 as an extension. The features +libc++ expects Clang to provide are: + +* Variadic templates. +* RValue references and perfect forwarding. +* Alias templates +* defaulted and deleted Functions. +* reference qualified Functions + +There are also features that Clang *does not* provide as an extension in C++03 +mode. These include: + +* ``constexpr`` and ``noexcept`` +* ``auto`` +* Trailing return types. +* ``>>`` without a space. + + +Provided C++11 Library Extensions +================================= + +.. warning:: + The C++11 extensions libc++ provides in C++03 are currently undergoing change. Existing extensions + may be removed in the future. New users are strongly discouraged depending on these extension + in new code. + + This section will be updated once the libc++ developer community has further discussed the + future of C++03 with libc++. + + +Using Minimal C++11 in libc++ +============================= + +This section is for developers submitting patches to libc++. It describes idioms that should be +used in libc++ code, even in C++03, and the reasons behind them. + + +Use Alias Templates over Class Templates +---------------------------------------- + +Alias templates should be used instead of class templates in metaprogramming. Unlike class templates, +Alias templates do not produce a new instantiation every time they are used. This significantly +decreases the amount of memory used by the compiler. + +For example, libc++ should not use ``add_const`` internally. Instead it should use an alias template +like + +.. code-block:: cpp + + template + using _AddConst = const _Tp; + +Use Default Template Parameters for SFINAE +------------------------------------------ + +There are three places in a function declaration that SFINAE may occur: In the template parameter list, +in the function parameter list, and in the return type. For example: + +.. code-block:: cpp + + template + void foo(_Tp); // #1 + + template + void bar(_Tp, enable_if_t* = nullptr); // # 2 + + template + enable_if_t baz(_Tp); // # 3 + +Using default template parameters for SFINAE (#1) should always be prefered. + +Option #2 has two problems. First, users can observe and accidentally pass values to the SFINAE +function argument. Second, the default arguement creates a live variable, which causes debug +information to be emitted containing the text of the SFINAE. + +Option #3 can also cause more debug information to be emitted than is needed, because the function +return type will appear in the debug information. + +Use ``unique_ptr`` when allocating memory +------------------------------------------ + +The standard library often needs to allocate memory and then construct a user type in it. +If the users constructor throws, the library needs to deallocate that memory. The idiomatic way to +achieve this is with ``unique_ptr``. + +``__builtin_new_allocator`` is an example of this idiom. Example usage would look like: + +.. code-block:: cpp + + template + T* __create() { + using _UniquePtr = unique_ptr; + _UniquePtr __p = __default_new_allocator::__allocate_bytes(sizeof(T), alignof(T)); + T* __res = ::new(__p.get()) T(); + (void)__p.release(); + return __res; + } diff --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst index 37b278f6a5c3..e2aca72edd57 100644 --- a/libcxx/docs/index.rst +++ b/libcxx/docs/index.rst @@ -142,6 +142,7 @@ Design Documents DesignDocs/ThreadingSupportAPI DesignDocs/FileTimeType DesignDocs/FeatureTestMacros + DesignDocs/ExtendedCXX03Support * ` design `_ * ` design `_ diff --git a/libcxx/test/libcxx/minimal_cxx11_configuration.pass.cpp b/libcxx/test/libcxx/minimal_cxx11_configuration.pass.cpp new file mode 100644 index 000000000000..7a9f55f569e2 --- /dev/null +++ b/libcxx/test/libcxx/minimal_cxx11_configuration.pass.cpp @@ -0,0 +1,135 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +// Test the set of C++11 features that Clang provides as an extension in C++03 mode. +// The language features we expect are: +// +// 1. rvalue references (and perfect forwarding) +// 2. variadic templates +// 3. alias templates +// 4. defaulted and deleted functions. +// 5. default values for non-type template parameters. +// +// Some features we don't get and can't be used in extended C++03 mode: +// +// 1. noexcept and constexpr +// 2. Two closing '>' without a space. + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wc++11-extensions" +#endif + +#include +#include + +// Equals delete and default are allowed in minimal C++03 mode. +namespace test_eq_delete_and_default { +void t1() = delete; +struct T2 { + T2() = default; + T2(T2 const&) = delete; +}; +} + +namespace alias_templates { +template +using X = T; +static_assert((std::is_same, int>::value), ""); +} + +namespace variadics_templates { +template +int t1(Args...) { + return sizeof...(Args); +} +void test() { + assert(t1() == 0); + assert(t1(42) == 1); + assert(t1(1, 2, 3) == 3); +} +} + +namespace rvalue_references_move_semantics { +struct T { + T() : moved(0) {} + T(T const& other) : moved(other.moved) {} + T(T&& other) : moved(other.moved) { ++moved; other.moved = -1; } + int moved; +}; +void f(T o, int expect_moved) { assert(o.moved == expect_moved); } +void test() { + { + T t; + assert(t.moved == 0); + T t2(static_cast(t)); + assert(t2.moved == 1); + assert(t.moved == -1); + } + { + T t; + f(t, 0); + f(static_cast(t), 1); + } +} +} + +namespace rvalue_references_perfect_forwarding { +template +void f(T&&) { + static_assert((std::is_same::value), ""); +} +void test() { + int x = 42; + f(x); + f(42); + f(static_cast(x)); +} +} + +namespace default_values_for_nttp { +template +void f() { assert(I == 42); } +void test() { + f(); +} +} + +namespace reference_qualified_functions { +struct T { + T() : lvalue_called(0), rvalue_called(0) {} + void foo() const & { lvalue_called++; } + void foo() && { rvalue_called++; } + mutable int lvalue_called; + int rvalue_called; +}; + +void test() { + { + T t; + t.foo(); + assert(t.lvalue_called == 1); + assert(t.rvalue_called == 0); + } + { + T t; + static_cast(t).foo(); + assert(t.lvalue_called == 0); + assert(t.rvalue_called == 1); + } +} +} + +int main(int, char**) { + variadics_templates::test(); + rvalue_references_move_semantics::test(); + rvalue_references_perfect_forwarding::test(); + default_values_for_nttp::test(); + reference_qualified_functions::test(); + return 0; +}