[CPP] Dealing with Types

Type alias

A type alias is a name that is a synonym for another type. Type aliases let us simplify complicated type definitions, making those types easier to use.
We can define a type alias in one of two ways. Traditionally, we use a typedef;

typedef

typedef double wages; // wages is a synonym for double
typedef wages base, *p; // base is a synonym for double, p for double*
The new standard introduced a second way to define a type alias, via an alias declaration

Alias declaration

using SI = Sales_item; //SI is a synonym for Sales_item
An alias declaration starts with the keyword using followed by the alias name and an =. The alias declaration defines the name on the left-hand side of the = as an alias for the type that appears on the right-hand side

pointers, const and type aliases

Declarations that use type aliases that represent compound type and const can yield surprising results. For example, the following declarations use the type pstring, which is an alias for the type char *:
typedef char * pstring;
const pstring cstr = 0; //cstr is a constant pointer to char
const pstring *ps; //ps is a pointer to a constant pointer to char
Note that the difference between:
const pstring cstr = 0 (pstring is a pointer to char, so here cstr is a constant pointer to char)
and
const char * cstr = 0(here const char is what the pointer points to)

auto

When we want to assign a value to a variable, we must know the exact type of the variable, but it is always difficult. Under the new standard, we can let the compiler figure out the type for us by using the auto type specifier. Auto tells the compiler to deduce the type from the initializer. By implication, a variable that uses auto as its type specifier must have an initializer
auto i = 0, *p = &i; // ok: i is int and p is a pointer to int
auto sz = 0, pi = 3.14; // error: inconsistent types for sz and pi

Automatic type deduction for functions in C++14

In C++ 14, the auto keyword was extended to be able to auto-deduce a function’s return type.
auto add(int x, int y)
{
    return x + y;
}
Since x + y evaluates to an integer, the compiler will deduce this function should have a return type of int.

trailing return syntax

C++11 also added the ability to use a trailing return syntax, where the return type is specified after the rest of the function prototype.
Why would you want to use this? One nice thing is that it makes all of your function names line up:
enter image description here
But it is of more use when combined with some advanced C++ features, such as classes and the decltype keyword.

decltype

The decltype can be used to determine the type of an expression at compile-type.
enter image description here
Although it may seem like auto and decltype will always deduce the same type, that isn’t the case, as shown by the following example:
enter image description here
Generally, if you need a type for a variable you are going to initialize, use auto. decltype is better used when you need the type for something that is not a variable, like a return type.

when decltype((variable)) double parentheses

double parentheses) is always a reference type
enter image description here

struct

example:
struct Sales_item {
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
The struct will start from struct keyword and the class name is after the struct.
Note there is a “;” after the struct block
struct Sales_data {/*...*/} accum, trans, *salesptr;
//It is the same as above but better
struct Sales_data {/*...*/};
Sales_data accum, trans, *salesptr;

Writing our own header files

Headers usually contain entities (such as class definitions and const and constexpr variables) that can be defined only once in any given file. However, the program might defined header file twice, we need to write our headers in a way that is safe even if the header is included multiple times.
C++ use the preprocessor to define header guards. Preprocessor variables have one of two possible states: defined or not defined. The #define directive takes a name and defines that name as a preprocessor variable. There are two other directives that test whether a given preprocessor variable has or has not been defined:
  • #ifdefine is true if the variable has been defined
  • #ifndefine is true if the variable has not been defined.
  • Also, #endif is necessary

namespace using declarations

Up to now, our program have explicitly indicated that each library name we use is in the “std” namespace. However, referring to library names with namespace can be cumbersome. Fortunately, there are easier ways to use namespace members. The safest way is a using declaration.
A using declaration lets us use a name from a namespace without qualifying the name with namespace_name::prefix.
It is in form of using namespace::name;
#include <iostream>
using std::cin;
int main(){
    int i;
    cin >> i; //ok, cin is a synonym for std::cin
    cout << i; //wrong: no using declaration for cout
    std::cout << i; //ok
}

Headers should not include using declarations

code inside headers ordinarily should not use using declarations. The reason is that the contents of a header are copied into the including program’s text. If a header has a using declaration, then every program that includes that header gets that same using declaration. As a result, a program that didn’t intend to use the specified library name might encounter unexpected name conflicts.

评论

此博客中的热门博文

[MLE] Linear Classification

[AIM] MetaHeuristics

[CS231] Neural Networks