A look at C++14 and beyond: Papers Part 3
published at 03.04.2013 15:20 by Jens Weller
Save to Instapaper Pocket
This is the 3rd part of my little series over the papers in the Pre-Bristol mailing. I have added "and beyond" to the title, as I decided to handle all papers with Part 2, and now will continue to do so. This edition will again feature a few highlights, and a lot of proposals from different areas of C++.
Also, please understand, that all of the papers here are proposals. None of them are voted into any standard yet, maybe Bristol will give us a hint about what to expect for C++14, maybe not. Still, the proposals will give an impression, on what C++14 could be like.
N3574 - Binding stateful functions as function pointers
This paper aims at improving the interoperability between C APIs and C++. Currently you cannot declare a template variable or in this case functionpointer, when its also declared extern "C";. This limits the options to have a generic solution in passing stateful functions as functions pointers to C. C can be seen here also as a intermediate layer, which connects for example C++ to Scriptinglanguages. The author proposes for this a std::bound_function type, which is described as:
"When the bound_function is constructed, it shall prepare a function pointer that, when executed, shall invoke the function object passed to it's constructor, forwarding all arguments. The conversion operator shall provide this function pointer. The bound_function shares ownership of the function pointed to by the function pointer between copies. The function object's destructor shall not throw an exception, else, the behaviour is undefined."
N3575 - Additional Standard Allocation Schemes
"The Standard provides an allocator abstraction, but does not provide any useful allocators beyond the default."
Thats what this paper would like to change. It aims at adding additional allocators to the Standard. The authors propose the following new allocators for addition to the standard:
- heap
- unserialized_heap_allocator
- object_pool
- unserialized_pool_allocator
- serialized_pool_allocator
- arena
- arena_allocator
- concurrent_arena
- concurrent_arena_allocator
As far as I can see those shall be added to the namespace std::memory, and most likely reside in <memory>. In my opinion it would be favorable to combine this proposal with N3525, polymorphic allocators.
N3578 - Proposing the rule of 5
C++ knows the rule of 3, being Copyconstructor, Assignment Operator and Destructor, which if one is defined explicitly, the others should be defined too. The authors propose
"that no copy function, move function, or destructor be compiler-generated if any of these functions is user-provided."
The rule of five functions are:
- copy constructor
- copy assignment operator
- move constructor
- move assignment operator
- destructor
N3579 - A type trait for signatures
This proposals aims adding signatures to type traits:
"We propose a (compiler-supported) type trait std::signature whose type typedef is the function type of the callable object when called with the given parameter types."
There is an extended example in the paper, which shows the application of such a signature type trait.
N3580 - Concepts Lite - Constraining templates with predicates
Well, this is one of the papers that might not be part of the next minor standard, which seems to disapoint a few of you out there. The concepts approach for C++11 failed, it was dropped, as it was to complex to be adopted fully to the standard for C++11. Since then a lot of people have had their thoughts on how to integrate concepts into the language, as its a feature that would enhance C++ for sure. This proposal now concentraits on template constraints, which shall be applied to force the correctness of template use, not definition. The idea is to
- let programmers directly state the requirements for template arguments as part of the templates interface
- support function overloading and class template specialization based on constraints
- fundamentally improving diagnostics by checking template arguments in terms of stated intent at the point of use
- do all of this without any runtime overhead or longer compile times
Sounds good, so lets see a little example:
template<Sortable Cont> void sort(Cont& container);
Here, Sortable is the constraint. It is in this case defined to require a random access iterator and the operator<. The Sortable constraint acts as type of Cont here, but is defined else where. So this is not the definition of a constraint, it is the use of a constraint. A alternative syntax allows to specifiy the constraints more exactly with require:
template<typename Cont> requires Sortable<Cont>() void sort(Cont& cont)
This allows to explicit state the requirements for a template after its interface definition. A constraint is now defined as:
"A constraint is simply an unconstrained, constexpr function template that takes no arguments and returns bool. It is —in the most literal sense— a predicate on template arguments. This also means that the evaluation of constraints in a requires clause is the same as constexpr evaluation."
So, a more complex constraint definition could look like this:
template<typename T> concept Equality_comparable() { return has_eq<T>::value && is_convertible<eq_result<T>,bool>::value && has_ne<T>::value && is_convertible<ne_result<T>,bool>::value; }
To aid the implementation of concepts, the authors also introduce a way to implement constraints as a requires expression:
template<typename T> constexpr bool Equality_comparable() { return requires (T a, T b) { bool = {a == b}; bool = {a != b}; }; }
This goes on for more details see the paper. The authors state, that constraints are not concepts, and that this is only the first step in improving the language at compile time, and that full featured constraints are still the long time goal for the C++ Standard. Regarding C++14, there is a goal of getting at least a minor version of concepts lite into the C++14 standard, at least this is stated in the Meeting Minutes from the last telco in March. Bristol might bring further clarification here. Also a little spoiler, there will be most likely a talk about this topic at Meeting C++ 2013!
N3581 - Delimited Iterators
Printing a vector of numbers with a ostream_iterator is tempting, but when used with a delimeter, the result is not as expected:
vector<int> v = {1, 4, 6}; cout << "("; copy(v.begin(), v.end(), ostream_iterator<int>(cout, ", ")); cout << ")"; // Oops! Prints (1, 4, 6, )
The delimeter in ostream_iterator behaves more like a suffix. The paper proposes now to options, to enable a more delimeter like behavoir for C++:
- add a bool parameter for suffix: ostream_iterator<int>(cout, ", ",false)// false = use as delimeter
- add a new delimeted_iterator<int>(cout, ", ")
N3582 - return type deduction for normal functions
With C++11 auto was introduced, and its widely used in function contexts such as lambdas. One can not declare the return type of a function yet as auto. This was planned, but has been in the effort to streamline C++11, dropped. This paper aims at adding this as planned to the standard, maybe for C++14. This is the 4th revision of an older paper, originaly aimed for C++11.
N3583 - Exploring constexpr at Runtime
This paper wants to extend constexpr, so that either it is possible to constraint certain constexpr functions and constructors to compile time only, or/and adding the ability of overloading a constexpr, so that the behavoir can be different at runtime then during compile time.
The author brings for each two cases an example. They present an constexpr method to calculate binary coded decimals from a const char* to a 32bit or 64bit integer. This code should only be executed at compile time. For the other option, they present a constexpr method of computing a sqrt from a value of different types. If this method is called at runtime, its muc slower as std::sqrt, so they argue, it would be favorable to be able replace the compiletime implementation with std::sqrt, if called at runtime.
N3584 - Wording for accessing Tuplefields by type
In Portland "Accessing Tuples by type" was accepted, but the wording is still pending, which is what this proposal contains. A short example of what is planned to be added to tuples:
tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7 int j = get<2>(t); // Equivalent to the above: j == 7 string s = get<string>(t); // Compile-time error. Ambiguous
N3585 - Iterator related Improvements to containers (Revision 2)
"This proposal recommends several small enhancements to the way containers interact with iterators. While none of these introduces functionality that cannot be achieved by other means,they make containers easier to use and teach, and make user code smaller and easier to read."
So, this proposal wants to ease the use of containers for the user. A short list of planned improvements:
- iterator last() // returns an iterator to the last element of a container
- make it possible to create an iterator of a container, without an instance of that container.
- add a type iterator to the value of pair<key,value> based containers
- add conversion between iterators and indices
N3586 - Splicing Maps and Sets
Node based containers like std::map or std::set are useful for storing large collections of large or unmovable objects. Also Maps can be used to create large database table like dictionarys, which store data bound to a key. With C++11 it is even possible to add objects to a map or set without creating a temporary copy with emplace. Still, it is not possible to easily move a node from one to another container. While std::list offers splice, there is no such functionality in the associative containers. This proposal aims to add splice functionality for maps and sets in C++. As technically a splice method is not possible for maps or sets, the goal is to add a new function called remove, which returns a unique pointer holding the node, and add a new overload to insert, allowing to insert the node into the new container. This still will have the need to delete the node from one container and insert into the other, but a copy is not needed.
N3587 - For loop exit strategies
This paper deals with for loops. Or, dealing with the condition after forloops. Sometimes you want to break iteration if a certain condition is met. And then react on this condition after the for loop. So you might have an if else block after the for loop, testing if the previous declared iterator is equal to end(container). This proposal aims to add an optional then/else block to forloops:
for(*;*;*)/for(:) { } then // end criteria was met, loop has fully run { } else // some condition triggered a break statement. { }
Now, this would need to make then a new keyword. Only for this purpose, that is why the author proposes a new statement, the if for:
if for(*;*;*)/for(:) { // normal loop body } {//then } else // break occured { }
N3588 make_unique
"This is a proposal to add make_unique for symmetry, simplicity, and safety."
Not much more to add. There is already make_shared, why not have make_unique.
N3589 Transactional Language Constructs for C++
This is the paper containing the current status for the Transactional Memory Study Group for the C++ Standard. The approach of seeing each function as transaction unsafe, and the requirement to mark transaction safe functions with transaction_safe were dropped in Portland. The authors now favor a model, where the compiler assumes that each function is transaction safe, unless it is marked as transaction_unsafe (or has calls to such functions). In the current model, compiler (single translation units) and linker (all object code) test for transaction safety.
This leads to 3 qualifiers:
- transaction_unsafe - mark functions containing transactionunsafe code
- transaction_safe - mark functions containing only transaction safe code, this will be tested by the compiler.
- __forbidden_in_atomic - marks a block of code not allowed to be executed in a transaction safe environment
Further the paper discusses the topic of nesting, exceptions and canceling, and how those interact with each other. The paper concludes, that the founding of the study group has brought in additional expertise, which will fuel the further work in this field.
N3591 - Discussions on Explicit Canceling on Transactional Language Constructs for C++
More transactional memory... This paper focuses on a special area, the explicit canceling of atomic/relaxed transactions. The paper states that it is yet not clear if the study group wants to support this for now or not. Canceling a transaction has the effect, that further code in the transaction is not executed, and also destructors, as they would be rolled back by the transaction cancellation anyway. As a whole, this topic is work in progress, without current stable results that could be presented in a proposal.
N3592 - Alternative cancellation and data escape mechanisms for transactions
While the previous paper just discussed the general option of canceling transactions, this paper goes more into the details. Canceling transactions is more then just saying "cancel", there are a number of questions that you should deal with, one of them is, how to possibly let data escape from the transactions, so that some results can be saved. Also this paper discusses why canceling and exceptions are not the same thing. It goes on to show a possible syntax for canceling, and how this could be integrated with the current concept of transactional language constructs. As the above paper stated, there is currently no favored option by the study group, so that this could just be one of the possible ways to handle this.
N3593 - std::split() - an algorithm for splitting strings
There is a proposal for a delimeter iterator, this is the opposite for strings. It aims at reading a list of values as a string, and turning them with a split function in a container of string_view. The algorithm builds up on std::string_view, a non owning reference to a std::string, a few usage examples:
vector<string_view> v{std::split("a-b-c", "-")}; deque<string_view> d{std::split("a-b-c", "-")}; set<string_view> s{std::split("a-b-c", "-")}; list<string_view> l{std::split("a-b-c", "-")};
std::split can return any range based STL container, and is in principle a function taking a Range and turning it into a Range of Ranges. It is specialized for strings, the authors currently don't favor a generic solution, as this would complicate the implementation.
N3594 - std::join: an algorithm for joining a range of elements
This is the inverse of the above tutorial, joining a range of ranges into a single range. As the above proprosal it concentrates on strings as the result, but can join arbitrary types into a delimeted string. It is extendable over a formatter function object. There are two versions of std::join proposed:
- std::join(const Range& range, std::string_view sep, Formatter f)
- std::join(const Range& range, std::string_view sep)
Where Range and Formatter are template arguments of the template function join. The second option uses a default formatter. The formatter object requires an operator()(std::string& output, T n), where T is the type to convert to string. Example usage:
std::vector<int> vi{1, 2, 3}; std::string svi = std::join(vi, "-"); assert(svi == "1-2-3");
N3595 - Simplifying Argument-Dependend Lookup Rules
"ADL rules as defined today are not always intuitive"
But maybe there options for simplifiying them, the paper names a few:
- Don't search in template argument namespaces
mtl::vector::densevector<boost::rational> v(12); std::cout << "size is " << size(v)<<'\n'; // should print 1
This will also search in boost:: for size(v). - Lookup in the arguments' namespaces can/must be requested explicitly with the attribute [[full_adl]].
- ADL with explicit arguments
alpha= dot(v, w); // #1 works nicely alpha= dot<8>(v, w); // #2 ADL is turned of
The explicit argument currently turns ADL of, this should not be the case here. - Inline friend functions are treated with the same priority as free functions.
- Explicitly turning off ADL
(f)(x, y, z); // Don't search f in the ns of x, y, z
This currently turns off ADL. The author proposes an attribute [[no_adl]] to turn off ADL explicitly.
There is currently no implementation for this. The advantage would be, that ADL is improved and less errormessages are produced trough ADL.
N3596 - Code Reuse in Class Template Specialization
The motivation behind this paper is, that currently you have to duplicate a lot of code in order to specialize a template class. While the specialization it self is a very powerful tool in C++, the duplication of code does not improve its usability. The author proposes to make a few changes to improve this, with full backward compability.
A short example, currently you could specialize a template like this:
tempalte<typename U>
class my_class<std::complex<U>> { typedef std::complexvalue_type; typedef size_t size_type; typedef my_class self; my_class(int i) : x(...), y(...){} value_type f1()const{} value_type& f2(){} size_type f3(){} value_type x; size_type y; };
The paper aims now at making this shorter and easier:
template<typename U> class my_class<std::complex<U>>= default { value_type f1()const = delete; value_type& f2(){} const value_type& f4(){} };
As this proposal handles the code reuse with new attributes, old code should not be effected, there fore full backward compability is given.
N3597 - Relaxing constraints on constexpr functions
Originally the body of constexpr function was constraint to { return expression; }, this was already for C++ relaxed last year at Portland, the authors of this paper want to further improve this. Currently the rules for constexpr allow:
- null statements
- static_assert
- typedef declarations and alias-declarations that do not define classes or enums
- using-declarations
- using-directives
- and excatly one (1) return statement
The authors propose to
"Promote constexpr to a largely unrestricted compile-time function evaluation mechanism. There is implementation experience of such a mechanism in the D programming language, where it is a popular feature"
And this is the end of part 3. Part 4 will contain the rest, 23 more papers to go.
Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!