C++ Pass Type Name As Argumentative Essays

Introduction

With the increasing popularity of the Standard C++ library and the STL, it is becoming more and more important for a C++ programmer to understand the underlying mechanics of generic programming in C++. In this article I will look into the little known details of template argument deduction, an area of the C++ language that a lot of programmers find difficult to comprehend and a knowledge of which is vital for using Standard C++ efficiently.

This article is intended for experienced C++ programmers who wish to know the exact and yet clearly explained details of how argument deduction is done in the C++ language. Deduction of template arguments is an incredibly vast area and covering it in detail and in all its entirety in one article would be impossible, so I chose to pick out the most frequently used contexts that call for template argument deduction and explain them in detail rather than gloss over all cases of use of argument deduction. The following uses of argument deduction are covered in this article: "Deducing template arguments from a function call expression", "Deducing template arguments when taking the address of a (member) function template specialization", "Deducing template arguments for explicit specialization of a (member) function template".

Template argument deduction only pertains to function templates. This means that whenever you reference a specialization of a class template, you need to explicitly specify all template arguments. I will therefore refer to function templates only in the remainder of the article.

Although there are different cases when the language calls on template argument deduction, they are all based on deducing template arguments from a type. So it is here that the discussion will start.

Deducing template arguments from a type

Template parameters come in three flavors: 1) template template parameters (associated with a class template, which I will denote as for the remainder of the article), 2) type template parameters (associated with a type, denoted as ), and 3) non-type template parameters (most often associated with a constant value of integral or enumeration type, denoted as ).

To deduce a template argument from a given type means that you have two types - a type-id that contains one or more template parameters, I will call such a type-id for the rest of the article (some combinations of s, s, and is within one are possible). For the moment I will omit the details of how is obtained, suffice to say that in most cases it is one of the function parameters of a function template declaration. The other type that you have is a type-id that comprises no template parameters, which I will call , and an attempt is made to find a class template for a , a type for , and a value for so that becomes identical to .

Example:

template<template<class> class TT, class T> T* alloc(TT<T>& a) { return a.allocate(16); }

For this template can be . If the type-id from which we want to deduce and is , then argument deduction deduces to be and to be . This results in becoming , i.e. identical to .

So far so good, but what kind of type can be and what combinations of s, s, and is are allowed in so that template argument deduction is possible on the template parameters that consists of? The first part of the question

<colgroup> <col> <col></colgroup> <thead> </thead> <tbody> </tbody>
P's most specific type kindAllowable form for P
Object type
Reference type
Array type
Function type
Pointer to data member type
Pointer to member function type
Class template specialization (class type)
Class template specialization (class type)

is easier to answer since for template argument deduction the language allows to be any valid C++ type (not a constant reference to constant function, of course). As for , only the following combinations are allowed:

Where designates any valid sequence of and qualifiers, opt stands for optional, designates a list of function parameters where at least one parameter is , designates a list of template arguments where at least one template argument is , and, finally, designates a list of template arguments where at least one template argument is . type refers to any C++ type which consists of no template parameters, i.e. it does not depend on the template parameters of the function template. The construct class-template-name designates a name of a class template that is not a template template parameter. For instance this is shown in the following code snippet, where can be and class-template-name is thus .

template<class T> T* alloc(std::allocator<T>& a);

A few explanations are necessary to make sense of the above. When there is more than one in some of the combinations shown, the s do not need to be the same type template parameter. A good example that demonstrates this is the standard object generator for pointers to member functions - the function template . Here is one of its declarations (the name is overloaded):

template<class R, class T, class A> mem_fun1_ref_t<R,T,A> mem_fun_ref(R(T::*)(A));

Here can be , which is a variant of .

The second point that must be made about the allowable combinations is that they can be applied recursively - any of the 24 forms shown can be used in place of in forming other permissible combinations, provided, of course, that the resulting form is a legal C++ type-id. For instance having a form of is composed of and and is accepted by template argument deduction (remember designates a list of function parameters where at least one parameter is , so is a variation on ). The following function template declaration clarifies this example further:

template<template<class> class Alloc, class C, class T> void dummy_alloc( void(C::*)(Alloc<T>,char)const);

Here one possible form of is . If the corresponding designates the type , then template argument deduction will deduce to be , to be , and to be .

The forms involving template template parameters , , require special elucidation. Each of these forms specifies the use of a class template , i.e. a class template specialization, meaning that whenever you use any of the above three combinations in , you need to supply the same number of template arguments as the declaration of specifies, unless the declaration of contains one or more default template arguments in which case the trailing template arguments can be omitted. Note that the angle brackets must be present even if all 's template parameters have defaults. The following example demonstrates the use of a template template parameter in where has default template arguments for all its parameters.

template<class T, template<class=T> class TT> > void foo(int(*arr)(TT<>));

In this example can be and given of template argument deduction will resolve to and to .

Although there is a form among the allowable choices for , it shouldn't be taken as a template template parameter with no template arguments. In fact it just means that the list of arguments doesn't contain any other template parameters and must contain non-dependent types, which is illustrated by the following example:

template<template<class, class> class Sequence> void empty_sequence(Sequence<int, std::allocator<int> >& seq) { seq.empty(); }

In this function template, can be , which is a variant of (it is allowed to treat the form as where and are type template parameters with being , see the notes to the table of allowable forms) where, as you can see, the type parameter is and is . Given a corresponding of , template argument deduction will be able to deduce to be , to be , and to be int in spite of the appearing twice within the . In fact it will deduce twice and, with above , each time to the same type.

But what happens if references a type template parameter more than once and the corresponding A supplies two different types for each occurrence of in ? For instance, in the previous example if had a form of , then template argument deduction would deduce to have type in one place and in the other. The language prohibits these scenarios and deduction fails for 's function template when the context supplies such an .

A careful look at the table of allowable forms reveals that it's not possible to form a qualified by using them (even if the use is recursive). This means that if you make qualified, template argument deduction is not possible for it. For example:

template<template<class, class> class Sequence, template<class> class Alloc, class T> void empty_sequence(T::Sequence<T, Alloc<T> >&);

In this code sample one possible is and because of this being qualified, template argument deduction is not possible regardless of the form that the corresponding has.

As I said before, in forming a standard conforming compiler is allowed to replace in any of the allowable forms with another form from the table, but no such provision is made for . There are only four forms that contain in the table, these are , , , and . And in all these forms is used as an expression that has the form of a simple identifier. Thus, by strictly following the rules it is not possible to get such that would contain in a form different from a simple identifier. It follows that if you have a wherein is used in an expression which doesn't have the form of a simple identifier, template argument deduction is not possible.

template<unsigned i> void foo(int(&p)[+i]);

In this example can be . And no matter what form the corresponding has, no argument deduction is possible for this .

In a similar manner, no recursive use of the allowable forms enables to get of, for example, this form: . This is illustrated in the following code snippet:

template<class T, template<class> class TT> void foo(int(*arr)[sizeof(TT<T>)]);

Which again means that should argument deduction be presented with such , it will not deduce any template parameters in it.

In general, a which is qualified or contains involved in an expression other than a simple identifier (that is, not used by itself) or contains any other template parameter used in an expression such as is conventionally called a nondeduced context. Having such a in a template declaration is not ill-formed, it's just that template argument deduction cannot deduce any template parameters in that regardless of the form of the corresponding . This effectively means that the template parameters that such comprises must be either explicitly specified or deduced elsewhere. I'd like to note here that it is not possible to get a nondeduced context by recursively applying only the allowable forms. When consulting the table of allowable forms you should regard a nondeduced conext as a non-dependent type, which is denoted there as type.

Now that I've explained the concept of deducing template arguments from a type, it is high time that I embark upon some of the contexts that call on template argument deduction.

Deducing template arguments when taking the address of a (member) function template specialization

It is not uncommon for C++ programmers to initialize an object of type pointer to (member) function with an expression referring to a (member) function. As is the case with non-template (member) functions, (member) function template specializations can be used in such contexts too, much like normal non-template functions:

ptrdiff_t count_chr(const char* str, const char ch) { // using the specialization of the standard // 'count' algorithm ptrdiff_t (* pf)(const char*, const char*, const char&) = std::count<const char*, char>; return (*pf)(str, str+std::strlen(str), ch); }

One thing to remember here is that there are no implicit conversions etween pointers to functions of different types and pointers to member functions of the same class of different types. There must be an exact match:

struct test { void update1(char*, char*); void update2(const char*, const char*); void update3(const char*, const char*, int=0); }; void testing() { // Error, no exact match void (test::* pmf1)(const char*, const char*) = &test::update1; // OK void (test::* pmf2)(const char*, const char*) = &test::update2; // Error, default arguments are not // part of function type void (test::* pmf3)(const char*, const char*) = &test::update3; }

Before plunging right into the nitty-gritty details of argument deduction, it would be helpful if I clarified the meaning of the term function template specialization, which has already been used a number of times in this article (member function template specializations follow the same conventions and will be omitted from the following discussion).

A function template specialization is a use of a function template name with a full set of template arguments that uniquely identifies exactly one specialization. For instance in the code example above is a specialization of the function template . Sometimes a set of template arguments is given explicitly in a specialization and the number of arguments in the set matches the number of template parameters of the corresponding template, sometimes the set contains fewer template arguments than the number of template parameters of the matching function template, and sometimes it contains no template arguments at all (even the angle brackets could be left out). Whenever some or all template arguments are omitted, they must be deducible from the context and the result of the deduction is a function template specialization. When a full set of template arguments is specified, no template argument deduction takes place.

The point is that a C++ programmer never deals with function templates themselves (except, for example, when they declare, define or explicitly specialize them). Most of the time it is a specialization of a given template that a programmer uses. Below an earlier code fragment has been modified to clarify the points that were made earlier:

ptrdiff_t count_chr(const char* str, const char ch) { // Using the specialization of the standard // 'count' algorithm without specifying // template arguments explicitly. // 1. Name lookup finds the function template // template<class InIter, class T> // typename // iterator_traits<InIter>::difference_type // count(InIter first, InIter last, // const T& val); // 2. Template argument deduction deduces the // template arguments to have types 'const // char*' and 'char' respectively. Thus // making the expression 'std::count' // equivalent to 'std::count<const char*, // char>' // ptrdiff_t (* pf)(const char*, const char*, const char&) = std::count; return (*pf)(str, str+std::strlen(str), ch); }

Returning to template argument deduction when taking the address of a (member) function template, what I need to explain is how 's and 's are formed when some template arguments are not specified. The answer is surprisingly simple - there is just one and one . is the type of the initializer expression (possibly converted to a type of pointer to function) and is the type of the object being initialized.

For instance in the code sample above:

P is typename iterator_traits<InIter>:: difference_type(*)(InIter, InIter, const T&),

which in terms of the conventions used in the table of allowable forms is a variant of .

is and template argument deduction will be seeking such types for and that will make identical to .

If the initializer expression was , only one template argument would have to be deduced (since one is specified explicitly), i.e. in that case would be:

typename iterator_traits<const char*>:: difference_type(*)( const char*, const char*, const T&)

It is easy enough to write the initializer in such a way that no matching specialization could be found. For example the initializer of the form will result in being:

typename iterator_traits<char*>:: difference_type(*)(char*, char*, const T&),

which doesn't enable template argument deduction to make such identical to the corresponding .

If the name of the function template whose address is being taken is overloaded, then there are as many s as there are overloaded function templates by that name and template argument deduction will be performed against all of them with the same A. Those templates for which deduction does not succeed will not be considered further. If the initializer expression does not contain the angle brackets, non-template functions will also be considered, i.e. if the namespace std contained a function declaration:

ptrdiff_t count(const char*, const char*, const char&);

this function would also be considered in the example above. It is then up to overload resolution to decide which function template specialization is the most specialized and choose it. Note that a non-template function is always preferred to any function template specialization, as a non-template function is considered more specialized than a corresponding function template specialization.

It is quite common for to contain multiple references to the same template parameter and my example with the standard algorithm showed it. The first reference was in the return type iterator_traits<InIter>::difference_type, which is a nondeduced context and is therefore ignored, meaning that must be deducible from its other occurrences in the . The second and the third appearances are the same and have the form InIter. As the corresponding parts of also constitute the same type , can be deduced from either part to be the same type. If they were different, for instance, if was , then would be deduced to be in one part and to in the other. Whenever such a situation arises, argument deduction fails. See the discussion on this issue in the previous chapter.

Deducing template arguments for explicit specialization of a (member) function template

Consider the following piece of code in which two overloaded declarations of the function template are defined:

template<class T> struct example { template<class U> // 1st member template int foo(U); template<class U> // 2nd member template int foo(U*); };

Now suppose I have an explicit specialization of the following form in the same translation unit:

template<> template<> int example<char>::foo(int*);

But which member function template is explicitly specialized here? One possible answer is that it is the second one, but is it really? The truth is that for explicit specializations of (member) function templates, template argument deduction is done in a way similar to that I covered in the previous chapter - is the function type of an explicit specialization named and 's are the function types of templates with name that are members of the same scope as the explicit specialization. Template argument deduction then makes an attempt to make each of the 's identical to (for more on that see the chapter "Deducing template arguments from a type" at the beginning of this article). Those templates for which argument deduction fails are not considered further. Those for which it does are submitted to overload resolution and the latter makes a decision as to which of the templates is more specialized than the others. When it can make this decision, the explicit specialization is considered to specialize the most specialized template, otherwise the explicit specialization declaration is ill-formed.

In the example shown is . Given that the explicit specialization is a member of the class scope , there are two 's: is , and is . For argument deduction succeeds with deduced to be . For it succeeds too resulting in having type . So both member function templates are submitted to overload resolution for finding out which of them is more specialized than the other, and the latter selects the second, meaning that the explicit specialization specializes the second member template.

When declaring an explicit specialization, it is possible to explicitly specify some or all the template arguments of the function template being specialized. The arguments will actually be applied to the templates of the same name and scope as the explicit specialization. This can come handy in influencing the result of argument deduction. For example if the same translation unit contains another explicit specialization of the form:

template<> template<> int example<char>::foo<int*>(int*);

Things will proceed as follows:

is ,
is ,
and is .

No argument deduction will take place at all since the two member templates have just one template parameter and it has been specified. Since now has a different type from the type of the explicit specialization it is no longer considered, thus the explicit specialization specializes the first member template.

Deducing template arguments from a function call expression

When name lookup finds a name in a function call expression to denote a function template or a (non-special) member function template and the call leaves one or more trailing template arguments unspecified or has a form without the angle brackets, template argument deduction comes into action:

int main() { int data[] = {3, 0, -1, -3, 16, }; // Name lookup finds the function template // template<class RandomAccessIterator> // void sort(RandomAccessIterator first, // RandomAccessIterator last); // // Template argument deduction deduces the // template argument to have type 'int*'. // This results in a call to the function // template specialization // 'void sort<int*>(int* first, int* last)'. std::sort(data, data + sizeof data/sizeof*data); std::vector<int, std::allocator<int> > container, empty; // Name lookup finds the member function // template // template<class InputIterator> // void vector<int,allocator<int> >::assign( // InputIterator first, InputIterator last); // // Template argument deduction deduces the // template argument to have type 'int*'. // This results in a call to the function // template specialization // 'void vector<int,allocator<int> >:: // assign<int*>(int*, int*)'. container.assign(data, data + sizeof data/sizeof*data); // Argument dependent name lookup finds the // namespace scope function template // template<class T, class Alloc> // bool operator!=(const vector<T,Alloc>&, // const vector<T,Alloc>&); // // Template argument deduction deduces T to // be 'int' and Alloc to be std::allocator. if(container != empty) container.clear(); }

Unlike the contexts requiring template argument deduction that I looked at before, which were all more alike than different in terms of how 's and 's were formed, this case is really distinctive and you will see why shortly. It is also the most frequently used form of template argument deduction, so I will back its description up with more case studies.

The distinction from what I showed earlier is that when deduction starts for a (member) function template: 1) there can be more than one , 2) the number of 's is always the same as the number of 's. In fact, any function parameter which depends on a template parameter that has not been explicitly specified (see the earlier discussion on function templates and function template specializations) constitutes a . If all template arguments are explicitly specified, there are no 's and no argument deduction takes place. If the i-th function parameter of a function template qualifies as (I will call it for the rest of the article), then the -th argument of the corresponding function call expression is (which I will call ).

Different to the cases I covered before, here every and can undergo a transformation before template argument deduction takes place. Below are the details of how this process goes:

First every is studied and:

  • if is a reference type, it is converted to the underlying type of the reference and is considered an lvalue[1];

  • if is an lvalue of array type and is not a reference type, the array-to-pointer conversion is applied to ;

  • if is an lvalue of function type and is not a reference type, the function-to-pointer conversion is applied to ;

  • if is not a reference type, top-level const and volatile qualifiers are removed from .

After that is done, every is examined and:

  • top-level const and volatile qualifiers (if any) are removed from ;

  • if is a reference type, the underlying type of is substituted for it.

For the rest of this section, whenever I mention or , I'll be referring to their transformed versions.

With the information from the previous paragraphs in mind and the example I presented at the beginning of this section, I can now show how 's and 's are formed for the expression , for which name lookup finds the function template as a possible candidate. This template has two function parameters, which depend on the template parameters. Both the function parameters are of reference type and are the same and hence the underlying type of the references is used in determining 's - , are . The corresponding 's are also the same - , are . Template argument deduction then deduces to be and to be , which results in a call to the function template specialization

bool operator!=<int,std::allocator>( const vector<int,std::allocator>&, const vector<int,std::allocator>&)

The fact that argument deduction makes each identical to the corresponding has a major implication on calling (member) function templates, for it leaves no room for user-defined conversions on 's. Here is an example:

#include <algorithm> #include <functional> #include <iostream> struct example { static void foo(int i) { std::cout << i << '\n'; } typedef void fun_t(int); operator fun_t*() const { return foo; } }; int main() { int data[] = {3, 0, -1, -3, 16}; example inst; // std::for_each(data, // data + sizeof data/sizeof*data, // std::ptr_fun(inst)); std::for_each(data, data + sizeof data/sizeof*data, example::foo); }

Let's look at the lines that were commented out and the function call expression in particular. What happens there is that name lookup finds the following two function templates in the scope of the namespace :

template<class Arg, class Res> pointer_to_unary_function<Arg,Res> ptr_fun(Res(*)(Arg); template<class Arg1, class Arg2, class Res> pointer_to_binary_function<Arg1,Arg2,Res> ptr_fun(Res(*)(Arg1,Arg2);

For the first function template there is one and one . is , which, in terms of the conventions used in the table of allowable forms, is a variant of . The corresponding is the type . It is obvious enough that there exist no such types for the template parameters and that would make identical to . There is, however, a user-defined conversion from an expression of type example to type . If it was selected (like in the case of nontemplate functions), argument deduction would deduce to be type and to be . But, as I said before, the language prohibits this and argument deduction fails. Quite similarly, template argument deduction fails for the second function template. This all leaves the set of candidates submitted to overload resolution empty, thus making the construct ill-formed in the context shown above.

Since you can always perform user-defined conversions inside the body of a function template, the restriction of precluding userdefined conversions on 's can be easily circumvented. And a nice example of that can, not surprisingly, be found in the Standard C++ library. Let's take a look at the function object generator , here is its possible implementation:

template<class BinOp, class T> binder2nd<BinOp> bind2nd(const BinOp& op, const T& second) { return binder2nd<BinOp>(op, typename BinOp::second_argument_type(second)); }

When called, this function template deduces to be whatever type the second function argument has (except that it will effectively strip the type of the second argument of the possible const qualifier) and then explicitly converts it to the type required. Here is a small program that shows how this works:

#include <algorithm> #include <functional> #include <iostream> #include <iterator> struct example { operator int() const { return 3; } }; int main() { int data[] = {3, 0, -1, -3, 16}; example inst; std::transform(data, data + sizeof data/sizeof*data, std::ostream_iterator<int>(std::cout, " "), std::bind2nd(std::modulus<int>(), inst)); }

There's also another way in which argument deduction from a function call expression differs from the cases I looked at in the previous sections. It differs in that it allows each to be not identical to the corresponding . The following is allowed:

  • can be more const/volatile qualified than ;

  • if is a pointer or pointer to member type, it can be a type to which an expression of type can be converted via a qualification conversion;

  • if has one of the following forms: , , , or it can be a type derived from .

Of course, providing that it is possible to find such types for the template parameters of that becomes identical to its , these three alternatives are not considered.

The example of the use of the function template that I just presented also demonstrates the case where 's are different from its corresponding 's. Indeed, in the function call expression is the type and is the type example. The corresponding 's are: is , is . And template argument deduction deduces to be and to be example, which results in a call to the function template specialization:

binder2nd<std::modulus<int> > bind2nd(const std::modulus<int>&, const example&)

Given the way the deduction of template arguments from a function call expression goes, some familiar techniques can give surprising results. For example, the extensively studied in this article function template is written in such a way that it does not allow either of its template parameters and to be deduced to be a const-qualified type. Think about it. The same is true of, for example, the function template from the standard header .

Conclusion

The three cases that I explained in this article are not the only ones when the language uses template argument deduction. The other cases of note are "Deducing template arguments of a conversion function template", "Partial ordering of (member) function templates", "Deducing template arguments for explicit instantiation of a (member) function template", and "Referencing a (member) function template specialization in a friend declaration". Although not covered in this paper, these cases are based on the same principles and I believe that the information given in this article will assist the interested reader in mastering them, should such a need arise. As a starting point I can say that the topics "Deducing template arguments for explicit instantiation of a (member) function template" and "Referencing a (member) function template specialization in a friend declaration" are nearly identical to what I explained in "Deducing Template Arguments for Explicit Specialization of a (member) Function Template."

In any case, I'm now working on a follow-up to this article wherein I plan to give details of how things stand with respect to "Deducing template arguments of a conversion function template" and "Partial ordering of function templates."

I welcome any feedback from readers, which you can send directly to me at the address below.

Declaring non-type template arguments with

Table of Contents

  1. Table of Contents
  2. What's New
  3. Introduction
  4. Motivation and Scope
  5. Impact On the Standard
  6. Design Decisions
  7. Technical Specifications
  8. Wording
  9. Acknowledgments

What's New

Changes since P0127R0

  • Added wording for a hypothetical post-Concepts draft of the Standard.

Introduction

This paper proposes allowing non-type template parameters to be declared with the placeholder type specifier. The desired effect is that the type of the corresponding non-type template argument be automatically deduced, much like similar syntax works for polymorphic lambdas. This more focused proposal follows EWG's recommendations from the Spring 2015 Lenexa meeting.

The existing workaround is that the type of a non-type template parameter must be explicitly specified, which leads to unnecessary verbosity and reduced flexibility when writing a template intended to take constant arguments of any type. Example:

The example makes use of to retrieve the type of (a compile-time constant) before passing both the type and the value of to . The goal is to be able to modify the declaration of such that the type of doesn't need to be passed as a separate template argument, resulting in this simpler instantiation:

This can be achieved by allowing use of the keyword in template parameter lists.

Motivation and Scope

Consider a generic function call logger for an application that provides callback function pointers to a library. The logger should print the name of the function, the argument values, and the result of a call to any callback. In order to avoid calling the logger from within a callback function (and thus having to modify each function to support the logger), the logger itself is passed to the library in place of the callback function, and the logger passes the arguments along to the callback function. This implies that the logger for a callback function must match the callback function's type so that the library can call it directly.

It is desirable that the instantiation syntax for the logger be simple; the following seems perfectly reasonable:

In order for this to work, must be a template that takes a function pointer and a character pointer as arguments. If the type of the function is fixed, this is no problem:

If the type of the function is not fixed, things get more complicated:

The instantiation syntax also gets more complicated, because the type of the function must be passed as an additional argument:

Using

The template parameter list syntax can be extended in a simple and natural way using the keyword to indicate that the type of a value parameter is deduced at the point of instantiation:

The usual type modifiers may be used to constrain the type of the value parameter without the use of partial specialization:

Partial specialization may be used to switch on the type of a value parameter:

Here is what the logger would look like using :

The function type no longer needs to be explicitly specified, which means the instantiation can go back to the desired form:

As variadic template parameters

When appears as the type specifier for a parameter pack, it signifies that the type for each corresponding argument should be independently deduced:

A list of homogeneous constant values can be constructed with the aid of :

Impact On the Standard

The proposed feature adds no keywords and does not change the meaning of any existing code.

Opportunity cost

There is an opportunity cost associated with adopting this particular meaning for the keyword in this context. It has been suggested that could be used to allow for template parameters accepting any kind of template argument, be it a type, a value, a template, or any other construct that templates may accept at any point in the future.

Such a feature is desirable, but the use of the keyword for it is not. There is no existing context in which acts as anything other than a stand-in for a type name; consistency with the rest of the language dictates that behave as spelled out in this paper.

Compatibility with Concepts

This proposal is intended to be fully compatible with the Concepts TS. Because Concepts introduces new rules for dealing with placeholders, care had to be taken to avoid impacting any features of Concepts. The wording provided in this proposal is careful to specify that the rules introduced here are only applicable to placeholders designated by or . Concepts does not establish any new behaviors for that conflict with this proposal.

Design Decisions

A few people have suggested that all values in a parameter pack introduced by should have the same type. The rationale seems to be that because can be replaced by a single type name in a multiple variable definition, the same should be true here:

This approach is comparatively inflexible, in that it does not allow variadic lists of heterogeneous values. Additionally, the behavior specified in this document mirrors the existing behavior of the and keywords in this context:

Technical Specifications

  • The keyword, when it appears in a template parameter list, signifies that the associated template parameter is a value parameter and that the type of the value parameter is to be deduced at the point of template instantiation.
  • The keyword, when it introduces a template parameter pack, signifies that each element in the parameter pack is a value parameter and that the types of the value parameters are to be independently deduced at the point of template instantiation.
  • Type deduction for template parameters introduced with the keyword follows the same rules as specified for function template argument type deduction.

Wording

Relative to the Working Draft

All modifications are presented relative to N4567.

Modify §7.1.6.4 [dcl.spec.auto] paragraph 5:

A placeholder type can also be used in declaring a variable in the of a selection statement (6.4) or an iteration statement (6.5), in the in the or of a (5.3.4), in a , in declaring a static data member with a brace-or-equal-initializer that appears within the member-specification of a class definition (9.4.2).

Modify §7.1.6.4 [dcl.spec.auto] paragraph 7:

When a variable declared using a placeholder type is initialized, or a statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type variable type is determined from the type of its initializer. In the case of a with no operand or with an operand of type , the declared return type shall be and the deduced return type is . Otherwise, let be the declared type of the variable or return type of the function. If the placeholder is the , the deduced type is determined using the rules for template argument deduction. If the initialization is direct-list-initialization then the shall contain only a single . If the deduction is for a return statement and the initializer is a (8.5.4), the program is ill-formed. Otherwise, obtain from by replacing the occurrences of with either a new invented type template parameter or, if the initialization is copy-list-initialization, with . Deduce a value for using the rules of template argument deduction from a function call (14.8.2.1), where is a function template parameter type and the corresponding argument is the initializer, or in the case of direct-list-initialization. If the deduction fails, the declaration is ill-formed. Otherwise, the type deduced for the variable or return type is obtained by substituting the deduced into . [ Example:

end example ]

Example:

The type of is the deduced type of the parameter in the call of the following invented function template:

end example ]

If the placeholder is the , shall be the placeholder alone. The type deduced for is determined as described in 7.1.6.2, as though the or of the or the of the statement had been the operand of the . [ Example:

end example ]

Modify §14.1 [temp.param] paragraph 4:

A non-type shall have one of the following (optionally ) types:

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,

Modify §14.3.2 [temp.arg.nontype] paragraph 1:

A for a non-type shall be a converted constant expression (5.20) of the type of the . For a non-type of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject (1.8),
  • a temporary object (12.2),
  • a string literal (2.13.5),
  • the result of a expression (5.2.8), or
  • a predefined variable (8.4.1).

Note: If the represents a set of overloaded functions (or a pointer or member pointer to such), the matching function is selected from the set (13.4). —end note ]

Modify §14.3.2 [temp.arg.nontype] paragraph 2:

Example:

end example ]

Modify §14.5.5 [temp.class.spec] paragraph 8 list item 8.2:

The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example:

end example ]

Modify §14.6.2.2 [temp.dep.expr] paragraph 3:

An is type-dependent if it contains

  • an associated by name lookup with one or more declarations declared with a dependent type,
  • an associated by name lookup with one or more declarations of member functions of the current instantiation declared with a return type that contains a placeholder type,
  • the (8.4.1), where any enclosing function is a template, a member of a class template, or a generic lambda,
  • a that is dependent,
  • a that specifies a dependent type, or
  • a or a that names a member of an unknown specialization;

or if it names a dependent member of the current instantiation that is a static data member of type “array of unknown bound of T” for some T (14.5.1.3). Expressions of the following forms are type-dependent only if the type specified by the , or is dependent, even if any subexpression is type-dependent:

Modify §14.8.2.5 [temp.deduct.type] paragraph 5:

The non-deduced contexts are:

  • The of a type that was specified using a .
  • The of a .
  • A non-type template argument or an array bound in which a subexpression references a template parameter.
  • A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
  • A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions (13.4), and one or more of the following apply:
    • more than one function matches the function parameter type (resulting in an ambiguous deduction), or
    • no function matches the function parameter type, or
    • the set of functions supplied as an argument contains one or more function templates.
  • A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have a type for which deduction from an initializer list is specified (14.8.2.1). [ Example:end example ]
  • A function parameter pack that does not occur at the end of the .

Relative to the Concepts TS

All modifications are presented relative to ISO/IEC 14882:2014 (N4141) as amended first by the Concepts TS (N4553) and then incorporating changes to the working draft up through N4567.

Add a new paragraph after §7.1.6.4 [dcl.spec.auto] paragraph 8:

A placeholder type designated by or can also be used in the in the of a (14.1).

Modify §7.1.6.4 [dcl.spec.auto] paragraph 10 (paragraph 11 here):

When a variable declared using a placeholder type is initialized, or a statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type variable type is determined from the type of its initializer. In the case of a with no operand or with an operand of type , the declared return type shall be and the deduced return type is . Otherwise, let be the declared type of the variable or return type of the function. If the placeholder is the , the deduced type is determined using the rules for template argument deduction. If the initialization is direct-list-initialization then the shall contain only a single . If the deduction is for a return statement and the initializer is a (8.5.4), the program is ill-formed. Otherwise, obtain from by replacing the occurrences of with either a new invented type template parameter or, if the initialization is copy-list-initialization, with . Deduce a value for using the rules of template argument deduction from a function call (14.9.2.1), where is a function template parameter type and the corresponding argument is the initializer, or in the case of direct-list-initialization. If the deduction fails, the declaration is ill-formed. Otherwise, the type deduced for the variable or return type is obtained by substituting the deduced into . [ Example:

end example ]

Example:

The type of is the deduced type of the parameter in the call of the following invented function template:

end example ]

If the placeholder is the , shall be the placeholder alone. The type deduced for is determined as described in 7.1.6.2, as though the or of the or the of the statement had been the operand of the . [ Example:

end example ]

Modify §14.1 [temp.param] paragraph 5:

A non-type shall have one of the following (optionally ) types:

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,

Modify §14.4.2 [temp.arg.nontype] paragraph 1:

A for a non-type shall be a converted constant expression (5.20) of the type of the . For a non-type of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject (1.8),
  • a temporary object (12.2),
  • a string literal (2.13.5),
  • the result of a expression (5.2.8), or
  • a predefined variable (8.4.1).

Note: If the represents a set of overloaded functions (or a pointer or member pointer to such), the matching function is selected from the set (13.4). —end note ]

Modify §14.4.2 [temp.arg.nontype] paragraph 2:

Example:

end example ]

Modify §14.6.5 [temp.class.spec] paragraph 9 list item 9.2:

The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. [ Example:

end example ]

Modify §14.7.2.2 [temp.dep.expr] paragraph 3:

An is type-dependent if it contains

  • an associated by name lookup with one or more declarations declared with a dependent type,
  • an associated by name lookup with one or more declarations of member functions of the current instantiation declared with a return type that contains a placeholder type,
  • the (8.4.1), where any enclosing function is a template, a member of a class template, or a generic lambda,
  • a that is dependent,
  • a that specifies a dependent type, or
  • a or a that names a member of an unknown specialization;

or if it names a dependent member of the current instantiation that is a static data member of type “array of unknown bound of T” for some T (14.6.1.3). Expressions of the following forms are type-dependent only if the type specified by the , or is dependent, even if any subexpression is type-dependent:

Modify §14.9.2.5 [temp.deduct.type] paragraph 5:

The non-deduced contexts are:

  • The of a type that was specified using a .
  • The of a .
  • A non-type template argument or an array bound in which a subexpression references a template parameter.
  • A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
  • A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions (13.4), and one or more of the following apply:
    • more than one function matches the function parameter type (resulting in an ambiguous deduction), or
    • no function matches the function parameter type, or
    • the set of functions supplied as an argument contains one or more function templates.
  • A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have a type for which deduction from an initializer list is specified (14.9.2.1). [ Example:end example ]
  • A function parameter pack that does not occur at the end of the .

Acknowledgments

Numerous people gave constructive feedback regarding the use of in template parameter lists in an isocpp.org discussion thread.

Special thanks to Mike Spertus and Gabriel dos Reis for their invaluable analysis and assistance.

Thanks also to Andrew Sutton for assistance and instruction in identifying potential areas of conflict with the Concepts TS.

0 thoughts on “C++ Pass Type Name As Argumentative Essays

Leave a Reply

Your email address will not be published. Required fields are marked *