"Effective modern C + +" translation-Clause 3: Understanding Decltype

Source: Internet
Author: User

Article 3: Understanding Decltype

Decltype is a very interesting monster. If you provide a name or an expression, the Decltype keyword will tell you the name or the type of the expression. Usually, the results match your expectations. Sometimes, however, the results of Decltype lead you to the point where you go through reference books or ask questions on the web.

Let's start with the usual situation-there's no hidden surprises here. In relation to what happens in the template type deduction and auto type deduction, the Decltype keyword is like a parrot, and the derivation of a variable name or expression type has no change with template type deduction and auto type deduction:

const int i =0;   Decltype (i) is const intbool F (const widget& W);  Decltype (W) is a const widget&//decltype (f) is bool (const widget&) struct point{int x,                Y                         Decltype (point::x) is int};                  Decltype (point::y) is Intwidget W; Decltype (W) is Widgetif(f (W))...Decltype (f (w))) is Booltemplate<typenameT>//simplified version fo std::vectorclass vector{public:...  T& operator[] (std::size_t index);...};vector<int>v; Decltype (v) is vector<int>...if(v[0] ==0)...Decltype (V[i]) is int&

See, there's no surprises.

In C ++11, the primary use for Decltype is to declare a function template, where the return type of the function depends on its parameter type. For example, suppose we want to write a function that supports the user's container operation by using square brackets (that is, "[]") to add an index, then authenticating, and then reopening the result of the index. The return type of the function should be the same as the type returned by the index operation.

The [] operator typically returns T&,STD when it is on a container that is an element of T::d Eque is like this, std::vector is almost the same. The only exception is for std::vecotr<bool> , the [] operator does not return a bool&. Instead, it returns a completely new object, clause 6 will explain why, but it is important to remember that the return type of the [] operator acting on the container depends on the container itself.

Decltype made it simple. Here is the first version we wrote, showing how to derive the return type using Decltype. This template can be streamlined, but for the moment we don't do it:

template<typenametypename Index>     //works, butauto autoAnadAccess(Container& c, Index i)       //requires  decltype(c[i])                              //refinement{  authenticateUser();  return c[i];}

The auto before the function name has no correlation to the type derivation result. It implies that C++11 's tracking return type (trailing return) semantics is being used, for example: The return type of the function will be declared after the argument list (after). The advantage of tracing the return type is that the parameters of the function can be used in the declaration of the return type. For example, in authandaccess, we use C and I to specify the return type of the function, and if we want to declare the return type in front of the function name, like a traditional function, C and I cannot be used because they are not yet declared.

Using this declaration, as we expected, Authandaccess returns the return type when the [] operator is acting on the container.

C++11 allows derivation of the return type of a lambda for a single statement, c++14 extends this so that the return type of lambda and all functions (including functions with multiple statements) can be deduced. This means that in c++14 we can omit the trace return type (trailing return), leaving only auto, in this form of declaration, auto means that the type deduction will occur. In particular, it means that the compiler will derive the function's return type from the implementation of the function:

template<typenametypename Index>   //C++14 only, andauto authAndAccess(Container&c, Index i)       //not quite{  authenticateUser();  return c[i];                                 //return type deduced from c[i]}

But what kind of C + + type inference rules will be used? Is the type deduction rule for the template or auto, or Decltype?

It may be surprising that functions with auto return type deduce rules using template type. Although it seems that the type deduction rules for Auto are more consistent with this semantics, the template type deduction rules and the auto type deduction rules are almost identical, except that the template type deduction rules fail when confronted with the initializer of the curly braces.

In this case, it is problematic to infer the return type of authandaccess using the template type deduction rules, but the auto type derivation rules are not much better, and the difficulty stems from their handling of lvalue expressions.

As we discussed before, most [] operators return a t& on a container with the element T, but clause 1 explains that during template type deduction, the reference portion of the initialization expression is ignored and the following customer code is considered. Authandaccess is used with the auto return type, which derives its return type using the template type deduction:

std::deque<int>510;    //authenticate user, return d[5], then assign 10 to it;                             //this won‘t compile

Here, D[5] will return int&, but using auto type deduction to derive the function authandaccess will remove the reference and return an int type. In this way, the int return value becomes the return value of the function, which is an rvalue, and the code above attempts to assign 10 to a right value. In C + +, such an assignment is denied, so the above code will not compile successfully.

The problem is that we are using a template type deduction rule, which discards the reference qualifier in the initialization expression. So in this case, what we want is the Decltype type rule, and the Decltype type deduction allows us to make sure that the type authandaccess return and the expression C[i] type are exactly the same.

The author of C + + rules anticipates that in some cases type derivation requires the use of the Decltype type deduction rule, so the decltype (auto) specifier appears in c++14, which may appear to be somewhat contradictory at first (Decltype and auto?). But in fact they are perfectly reasonable, auto illustrates that the type needs to be deduced, decltype that the Decltype type deduction should be used in the derivation, so the authandaccess code would be the following:

template<typenametypename Index>    //C++14 only;decltype(auto)                                  //works, butautoAndAccess(Container&c, Index i)             //still requires{                                               //refinement  authenticateUser();  return c[i];}

In this way, the return type of the function autoandaccess is consistent with c[i]. In particular, when C[i] returns a t&, Authandaccess also returns a T&, and when C[i] returns an object, Authandaccess returns an object.

The use of the Decltype (auto) is not limited to the return type of the function, and you can use it to declare a variable when you want to infer the initializer with the Decltype type deduction:

Widget w;const Widget& cw = w;auto myWidget1 = cw;            //auto type deduction;                                //myWidget1‘s type is Widgetdecltype(auto) myWidget2 = cw;  //decltype type deduction:                                //myWidget2‘s type is                                // const Widget&

But I know there are two things that are bothering you. One is what I mentioned earlier why authandaccess still needs to be improved, now let's fill this section.

Let's take another look at the Authandaccess function declaration under the C++14 version:

template<typenametypenamedecltype(auto) authAndAccess(Container& c, Index i);

A container is passed in with a very constant reference to an lvalue, because returning a reference to an element in a container allows us to modify the container, but this means that we cannot pass an rvalue container to the function, and the right value is not bound to an lvalue reference (unless it is a constant lvalue reference, But not in this case)

Admittedly, passing an rvalue container to authandaccess is a boundary condition, an rvalue container that, as a temporary object, will be destroyed after the statement that contains the Authandaccess function call ends. This means that a reference to an element in the container (which is usually returned by the authandaccess function) will be suspended at the end of the call statement. However, it makes sense to pass a temporary object into Authandaccess, where a customer may simply want to copy an element from this temporary container, for example:

std::deque<std::string> makeStringDeque();     //factory function//make copy of 5th element of deque returned//from makeStringDequeauto5);

To support this use, it means that we need to modify the declaration of C so that it can accept both Lvalue and rvalue, which means that C needs to be universal reference (see clause 26)

template<typenametypenamedecltype(auto) authAndAccess(Container&& c, Index i);

In the above template, we do not know what type of container we operate on, and it also means that we ignore the type of element that the container subscript corresponds to. Passing a value to an unknown object usually requires an unnecessary copy, an object being split (see clause 17), and ridicule from a colleague. But according to the examples in the standard library (for example, Std::string,std::vector and std::d eque), it seems reasonable in this case, so we still insist on passing by value.

Now to do is to update the implementation of the template, according to the warning in clause 27, using Std::forward to implement the Universal reference:

template<typenametypename Index>       // finaldecltype(auto)                                     // C++14  authAndAccess(Container&& c, Index i)              // version  {   authenticateUser();   returnstd::forward<Container>(c)[i]; }

The above code accomplishes everything we want, but only if we need a compiler that supports c++14. If you do not have a compiler that supports C++14, you should use the template type in c++11. Except that you need to be clear about the return type, the other is no different from c++14:

template<typenametypename Index>       // final auto                                               // C++11  authAndAccess(Container&& c, Index i)              // version   decltype(std::forward<Container>(c)[i]) {   authenticateUser();   returnstd::forward<Container>(c)[i]; }

Another question that deserves your nagging. I have already marked the beginning of this article, and it is hardly surprising that Decltype's results are almost as good as you expect. To be honest, unless you want to implement a very large library, you are hardly likely to encounter exceptions to this rule,

To fully understand Decltype's behavior, you need to familiarize yourself with some special situations. Most of the books prove to be very obscure in this book, but one of them can make us understand more about the use of decltype.

But like I said, using Decltype for a variable name produces the type when the variable is declared. The name is an lvalue expression, but this does not affect the behavior of decltype. Because for an lvalue expression that is more complex than the variable name, Decltype ensures that the inferred type is always a reference to an lvalue. This means that if an lvalue expression differs from the type of the variable name t,decltype the deduced type will be t&. This is almost impossible to influence because most lvalue expressions have a qualifier inside the type that typically contains an lvalue reference. For example, a function that returns an lvalue always returns a reference.

Here is a notable place to

int x=0;

In X is the name of a variable, so the result of Decltype (x) is int. But wrap the name x in parentheses, "(x)" produces a more complex expression than the name, as a variable name, X is an lvalue, C + + also defines (x) is also an lvalue, so the Decltype ((x)) result is int&. By wrapping a variable in parentheses, the original result of the Decltype is changed.

In C++11, this is only a bit odd, but with the support of Decltype (auto) in c++14, some simple changes to the return statement will affect the result of the function's final derivation:

decltype(auto) f1()  {   int0;  …  return x;        // decltype(x) is int, so f1 returns int decltype(auto) f2() {   int0;  …   return (x);      // decltype((x)) is int&, so f2 returns int& }

Note that F2 and F1 are not just different on return types, F2 returns a reference to a local variable! The consequence of this code is to cause undefined behavior, which is certainly not what you want to happen.

This is mainly about using Decltype (auto). However, it is important to note that some seemingly insignificant details affect the results derived by Decltype (auto). To ensure that the type of the export being pushed as you expect, you can use the technology described in Clause 4.

But at the same time, don't lose focus on the big picture. The results of decltype (whether used alone or in conjunction with auto) may occasionally be surprising, but this does not happen very often. In general, the Decltype result is the same as the type you expect, especially if Decltype is applied to the variable name, because in this case, the Decltype is to provide the declaration type of the variable.

Please remember:

? Decltype typically always returns the variable name or the type of the expression without any modification.

For an lvalue expression different from the variable name, the result of Decltype is always t&.

? C++14 provides support for Decltype (auto), such as auto, which infers the type from its initialization, but uses the derivation rules of Decltype.

"Effective modern C + +" translation-Clause 3: Understanding Decltype

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.