Implicit Evaluation of auto Variables and Arguments

Similar documents
Implicit Evaluation of auto Variables and Arguments

I m sure you have been annoyed at least once by having to type out types like this:

Type Inference auto for Note: Note:

Lambda Correctness and Usability Issues

American National Standards Institute Reply to: Josee Lajoie

Let return Be Direct and explicit

register lock_guard(mtx_); string_view s = register to_string(42); We propose register-expression to grant the temporary objects scope lifetimes.

An Alternative to Name Injection from Templates

Structured bindings with polymorphic lambas

Wording for lambdas in unevaluated contexts

Expansion statements. Version history. Introduction. Basic usage

Overview. 1. Expression Value Categories 2. Rvalue References 3. Templates 4. Miscellaneous Hilarity 2/43

Class Types in Non-Type Template Parameters

C++11 and Compiler Update

Rvalue References as Funny Lvalues

Object-Oriented Principles and Practice / C++

Chapter 7 Constructors and Other Tools. GEDB030 Computer Programming for Engineers Fall 2017 Euiseong Seo

Modern and Lucid C++ Advanced for Professional Programmers. Part 3 Move Semantics. Department I - C Plus Plus Advanced

Class Types in Non-Type Template Parameters

Making operator?: overloadable

Part X. Advanced C ++

Upcoming Features in C# Mads Torgersen, MSFT

From Java to C++ From Java to C++ CSE250 Lecture Notes Weeks 1 2, part of 3. Kenneth W. Regan University at Buffalo (SUNY) September 10, 2009

CS 251 INTERMEDIATE SOFTWARE DESIGN SPRING C ++ Basics Review part 2 Auto pointer, templates, STL algorithms

New wording for C++0x Lambdas

QUIZ. What is wrong with this code that uses default arguments?

Appendix. Grammar. A.1 Introduction. A.2 Keywords. There is no worse danger for a teacher than to teach words instead of things.

Tokens, Expressions and Control Structures

1/29/2011 AUTO POINTER (AUTO_PTR) INTERMEDIATE SOFTWARE DESIGN SPRING delete ptr might not happen memory leak!

These new operators are intended to remove some of the holes in the C type system introduced by the old C-style casts.

Override Control Using Contextual Keywords

CPSC 427a: Object-Oriented Programming

Variables. Data Types.

CPSC 427: Object-Oriented Programming

Object-Oriented Programming for Scientific Computing

Lecture 8. Exceptions, Constructor, Templates TDDD86: DALP. Content. Contents Exceptions

Chapter 7. Constructors and Other Tools. Copyright 2016 Pearson, Inc. All rights reserved.

Modules: ADL & Internal Linkage

Deducing the type of variable from its initializer expression (revision 4)

CPSC 427: Object-Oriented Programming

Instantiation of Template class

Abstract This paper proposes the ability to declare multiple variables initialized from a tuple or struct, along the lines of:

Overload Resolution. Ansel Sermersheim & Barbara Geller ACCU / C++ June 2018

Generalized Constant Expressions

C++11: 10 Features You Should be Using. Gordon R&D Runtime Engineer Codeplay Software Ltd.

A Proposal to Add a Const-Propagating Wrapper to the Standard Library

Abstract This paper proposes the ability to declare multiple variables initialized from a tuple or struct, along the lines of:

The Syntax of auto Declarations

Simplifying C++0x Concepts

Deducing the type of variable from its initializer expression (revision 3)

Short Notes of CS201

Interview Questions of C++

Deducing the type of variable from its initializer expression Programming Language C++ Document no: N1721=

Qualified std::function signatures

Template deduction for nested classes P0293R0

Overload Resolution. Ansel Sermersheim & Barbara Geller Amsterdam C++ Group March 2019

The goal of the Pangaea project, as we stated it in the introduction, was to show that

Project Compiler. CS031 TA Help Session November 28, 2011

CS201 - Introduction to Programming Glossary By

const-correctness in C++

Auxiliary class interfaces

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Pointers. 1 Background. 1.1 Variables and Memory. 1.2 Motivating Pointers Massachusetts Institute of Technology

The issues. Programming in C++ Common storage modes. Static storage in C++ Session 8 Memory Management

Fast Expression Templates

A Proposal to Add a Logical Const Wrapper to the Standard Library Technical Report

University of Technology. Laser & Optoelectronics Engineering Department. C++ Lab.

See the CS 2704 notes on C++ Class Basics for more details and examples. Data Structures & OO Development I

Lambdas in unevaluated contexts

C The new standard

C++ Coding Standards. 101 Rules, Guidelines, and Best Practices. Herb Sutter Andrei Alexandrescu. Boston. 'Y.'YAddison-Wesley

Auto - a necessary evil?

More on Functions. Lecture 7 COP 3014 Spring February 11, 2018

An Incomplete Language Feature

Programming computer vision in C / C++

Chapter 11 Object and Object- Relational Databases

Layout-compatibility and Pointer-interconvertibility Traits

Object-Oriented Principles and Practice / C++

A Taxonomy of Expression Value Categories

Functions and Recursion

CPSC 427: Object-Oriented Programming

Cpt S 122 Data Structures. Course Review Midterm Exam # 2

Operating Systems CMPSCI 377, Lec 2 Intro to C/C++ Prashant Shenoy University of Massachusetts Amherst

P1267R0: Custom Constraint Diagnostics

2 is type double 3 auto i = 1 + 2; // evaluates to an 4 integer, so it is int. 1 auto d = 5.0; // 5.0 is a double literal, so it

The C++ Programming Language, Core Working Group. Title: Unary Folds and Empty Parameter Packs (revision 1)

Function Declarations. Reference and Pointer Pitfalls. Overloaded Functions. Default Arguments

Operator Overloading. Gary Powell, Doug Gregor, Jaakko Jȧ. rvi

Object-Oriented Programming

Evolution of Programming Languages

Evaluation Issues in Generic Programming with Inheritance and Templates in C++

CS558 Programming Languages

G52CPP C++ Programming Lecture 20

std::optional from Scratch

Lambdas in unevaluated contexts

async and ~future (Revision 3)

Chapter 8. Operator Overloading, Friends, and References. Copyright 2010 Pearson Addison-Wesley. All rights reserved

Tutorial 7. Y. Bernat. Object Oriented Programming 2, Spring Y. Bernat Tutorial 7

Introduction to Move Semantics in C++ C and C

Protection Levels and Constructors The 'const' Keyword

Transcription:

Implicit Evaluation of auto Variables and Arguments Document number: N3748 Authors: Joël Falcou University Paris XI, LRI Peter Gottschling SimuNova Herb Sutter Microsoft Date: 2013-08-30 Project: Programming Language C ++, Evolution Working Group Reply to: Peter.Gottschling@simunova.com 1 Motivation Type detection for variables from expressions return type: auto x= expr; has proven high usability. However, it fails to meet most users expectations and preferences when proxies or expression templates (ET) are involved, e.g.: matrix A, B; // setup A and B auto C= A B; Many people would expect C to be of type matrix as well. Whether C is a matrix depends on the implementation of operator. In the case that the operator returns a matrix then C is a matrix. For the sake of performance, computationally expensive operators very often return an Expression Template and delay the evaluation to a later point in time when it can be performed more efficiently because we know the entire expression and where the result is stored so that we can avoid the creation and destruction of a temporary. The impact of this approach to the beforementioned example is that C is not of type matrix but of some intermediate type that was probably only intended to be used internally in a library. Moreover, we assume that even people who are aware of expression templates will very often prefer the evaluated object (here a matrix containing the product of A and B) and not an unevaluated or partially evaluated object (representing the product of A and B). We therefor need a mechanism to evaluate objects of certain types implicitly and sufficient control over this mechanism. 2 Goals The implicit evaluation shall: 1

2 1. Enable class implementers to indicate that objects of this class are evaluated in an auto statement; 2. Enable them to determine the type of the evaluated object; 3. Enable them to implement the evaluation; 4. Allow programmers to explicitly disable this evaluation; 5. Provide information about the return type; 6. Allow for efficient use as function arguments; 7. Establish a compact and intuitive notation; and 8. Maximize backward compatibility. 3 Solution 3.1 Implicit Evaluation For the sake of illustration, we start with a typical implementation of expression templates: class product expr; // forward declaration class matrix... matrix& operator=(const product expr& product) / perform matrix product here / ; class product expr... ; inline product expr operator (const matrix& A, const matrix& B) return product expr(a, B); int main() matrix A, B; // setup A and B auto C= A B; // type of C is thus product expr The auto variable C yield the type of the assigned expression which is in this case product expr not matrix. We could have written: matrix C= A B; and C would obviously be a matrix and also contain the evaluated product. In the example above, the anonymous object product expr(a, B) apparently represents the product of A and B and the product of two matrices is a matrix as well. Thus, to meet the users typical expectations/preferences, we need to express how we create from an object representing A B an object that actually is A B. It was suggested in the reflector to introduce an operator auto to enable this implicit evaluation. In the example above, we would expand the implementation of product expr:

3 class product expr public: product expr(const matrix& arg1, const matrix& arg2) : arg1(arg1), arg2(arg2) matrix operator auto() matrix R(num rows(arg1), num cols(arg2)); R= this; return R; private: const matrix &arg1, &arg2; ; The operator auto in this example serves three purposes: It indicates that an assignment of a product expr object to an auto variable causes an implicit evaluation. The auto variable will have type matrix. The operator also implements the evaluation. Thus, in the presence of this operator, the auto variable C will have type matrix and contain the product. (Depending on the matrix implementation, one could write the operator in one line but this is a secondary detail here.) 3.1.1 Default Implementation In the previous listing, the evaluation was realized by an assignment. Usually, there is an equivalent constructor, e.g.: class matrix... matrix(const product expr& product) / construct a new matrix by performing a matrix product / ; Such a constructor allows for short implementations of the operator auto, e.g.: class product expr //... matrix operator auto() return matrix( this); ; We propose considering this pattern as default implementation, i.e.: class some temporary //... result type operator auto() = default; ;

4 corresponds to: class some temporary //... result type operator auto() return result type( this); ; Discussion: Alternatively the default implementation could be: class some temporary //... result type operator auto() result type result; result= this; return result; ; However, this requires that the result type is default-constructable and that the temporary can be assigned to this default-constructed object (in the context of matrices, the assignment must be allowed to change the dimension of the target matrix). We believe that the primary implementation is more generally usable. 3.1.2 Alternative Syntax for the Operator Distinction by signature: In the context of return type deduction of normal functions (N3638), another operator auto was proposed which deduces its type from the actual return statement. Although we are afraid that the combination of type deduction and conversion operator is very dangerous, the presence of this feature in the standard would suggest another syntax: operator auto result type(); Then the two kinds of operator auto would be distinguishable by their signature. However, the distinction is not particularly eye-catching and we are afraid that the two different meanings of operator auto could a common source of confusion. Using declaration: Daveed Vandevorde proposed on the reflector to denote the automatic evaluation by using auto for better distinction from conversion operators. The example above would read: class product expr using auto= matrix; //... ; The actual evaluation can be implemented in both classes: Either in matrix with a constructor taking a single object of type product expr as argument; or

5 In product expr with a conversion operator towards matrix. 1 3.2 Disabling the Implicit Evaluation In several situations, the programmer will need the unevaluated object and still likes to use the automatic type deduction. For this purpose, we propose denoting the declaration with the keyword explicit: explicit auto D= A B; The object D contains whatever is returned by operator regardless of whether an operator auto exists or not, in our case the expression template product expr(a, B). Creating a non-explicit auto variable from D would of course trigger the evaluation: explicit auto D= A B; // not evaluated // now tune some parameters of D auto E= D; // now the expression is evaluated Avoiding the immediate evaluation in the first line allow clever programmers doing clever tricks with D by either twisting parameters of D or generating another expression template from D that can be evaluated more efficiently. In the reflector discussion, Nevin Liber suggested denoting the non-evaluated expressions by &&: auto&& D= A B; Although the notation is more concise, we prefer the more intentional explicit keyword. 3.3 Reflection To refer to the return type of the operator auto, we propose the following template alias: template <typename T> using auto result type=...; When no operator auto is define the template alias returns T. For the types from this proposal it would be therefor: Type expression auto result type<product expr> auto result type<matrix> Result matrix matrix Open question: Can we define auto result type with decltype? Or is this a job for the compiler? 3.4 Implicit Evaluation of Function Arguments When expression templates are passed to function templates the programmer needs the choice whether the argument is evaluated or kept as expression template. Keeping the ET as it is enables a whole universe of optimization by transforming the represented Abstract Syntax Tree (AST). On the other hand, the types for the ET provide usually a much more limited interface than the type 1 A weird question is: when a conversion operator has a deduced type, can we then write: using auto= auto;? To be fair, auto operator auto() would not be better. For short, the combination with automatic return type deduction in conversion seems inapt regardless of the notation.

6 of the evaluated expression so that many implementations are much simpler after evaluation (or possible at all). For instance, a print function would be rather cumbersome when implemented for product expr and we probably prefer doing it in terms of matrix. We can do this by introducing an auto variable: template <typename Expr> void print(const Expr& expr) auto A= expr; // A should now be a container, e.g. matrix // operate on A Here the first statement in this function assures that A is container with readily evaluated data whenever all expression templates are equipped with an operator auto. Conversely, all types without operator auto are assumed to be container and are just copied. The drawback of this approach is that we always create a new object even when expr is already a container. For the sake of efficiency, we want to avoid this surplus copy. 3.4.1 Examples To this end, we suggest the template alias auto or ref that (usually) evaluates to the result type of operator auto if defined; otherwise to a reference of the argument type: This alias allows us to avoid the copy: template <typename Expr> void print(const Expr& expr) // A is a container but not copied if expr is already a container const auto or ref<expr> A= expr; // operate on A In this example, we have no surplus copy: ETs are passed by reference and evaluated in the function whereas containers are referred twice (which certainly can be optimized away by compilers). However, there might be cases where even in the presence of an auto operator the evaluation creates an overhead. Say, we have a function on vectors like the cross product: template <typename Vector1, typename Vector2> inline auto cross(const Vector1& v1r, const Vector2& v2r) const auto or ref<vector1> v1= v1r; const auto or ref<vector2> v2= v2r; //... multiple read accesses on v1 and v2 This function shall be called with different types of arguments: auto z1= cross(v, w); // #1 two containers auto z2= cross(v, A w); // #2 expression template auto z3= cross(a[iall][2], w); // #3 proxy for matrix column The first case is that both arguments are containers and we simply want to refer them. In the second case, we have a matrix vector product as expression template which represents a column vector. Accessing an element of this ET would require a product of the matrix s row with the vector. So, it would be less expensive to compute the resulting vector once and use its

7 elements. The situation might be even worse when the ET doesn t even provide an element access, e.g. when the matrix is a column-wise compressed matrix (and providing a single result entry is as expensive as the complete operation). For short, expression templates should be evaluated in any case. In the third case is the most difficult. We assume that the access to a matrix column is realized by a proxy (which provides an auto operator). Using the proxy directly might be more efficient than paying the price of creating and destroying a temporary vector. However, whether evaluating the proxy is beneficial might depend on the type of object that the proxy refers to and even on the platform the executable is running. We therefor suggest to provide the user the ability to decide when a type used as function argument is evaluated. 3.4.2 Default Behavior and Specialization In order to enable user-defined behavior, we propose a two-step definition of auto or ref: 2 a type trait named auto or ref trait that determines whether a type should be evaluated and the template alias auto or ref based on it: template <typename T> struct auto or ref trait : conditional<!is same<t, auto result type<t>>::value, true type, false type>::type ; template <typename T> using auto or ref= typename conditional<auto or ref trait<t>::value, auto result type<t>, T&>::type; The first class determines the default behavior: For a type with an auto operator (i.e. when T is different from auto result type<t>), the trait class is derived from true type indicating that the object will be evaluated. Opposed to it, types without auto operator are derived from false type indicating that the object will only be referred. Programmers that want their proxy type not evaluated have to write: template <typename U> struct auto or ref trait<my proxy type<u>> : false type ; Of course, the type can implement arbitrarily complicated conditions. The template alias auto or ref will then be defined as either the evaluated type or a reference. 3.4.3 Mutable Arguments We refrain from distinguishing constant and mutable references, i.e. introducing an alias auto or const ref since we consider the choice between evaluating or referring orthogonal to constancy. When the reference is const then the object must not be modified in the algorithm, thus, we can also declare the evaluated object as const. 2 N1489 argues that template aliases should not be specialized.

8 On the other hand, the mutable version: template <typename Expr> void weird function(expr& expr) auto or ref<expr> A= expr; // modify A makes not really sense (nonetheless we are open to learn counter-examples). Of course, we can safely modify both the reference and the evaluated object. However, in the latter case the object is only a temporary in the function and the impact would not be visible from outside. 4 Backward Compatibility Programs containing statements like: auto x= expr; where expr is an expression template and x shall not contain the evaluated object need to be transformed into: explicit auto x= expr; after the type of expr was equipped with an operator auto. Conversely, several programs that do not work today because the programmer expected an evaluated will work in the future after operator auto is introduced. 5 Consistency No matter what semantics we choose, we do want to end up where these cases are consistent: // case 1: local variable auto x = expr; // case 2: function parameter template<class T> void f( T x ); f( expr ); // case 3: lambda parameter auto f = []( auto x ) ; f( expr ); Today these are identical (except only that case 1 can deduce initializer list, and there is some pressure to remove that inconsistency). We believe these should stay identical. The problem can be approached from two sides: Types with defined auto operator are always evaluated unless auto or T is decorated with explicit. This implies that explicit must be allowed for generic parameters of named functions and lambdas. The drawback of this approach is that function calls with evaluatable types as arguments would be even incompatible to C ++ 98. Types with defined auto operator are only evaluated if auto or the template parameter is decorated with implicit. The drawbacks of this notation is that the variable declaration

9 with implicit evaluation would be more verbose and that we would introduce a new keyword. At least, this should not break existing code since free names are not allowed in front of auto or a template parameter. Both approaches are not satisfactory and we still consider it an open question how to achieve this in the presence of implicit evaluation. 6 Summary We proposed a user-friendly method to deal with expression templates and proxies for local variables by introducing an implicit evaluation. There are still some open questions for which we hope to find an answer soon. 7 Wording To be done!