casyup.me@outlook.com

0%

read/newFeaturesInC++14

new features in c++14

From : wiki

New language features[edit]

These are the features added to the core language of C++14.

Function return type deduction[edit]

C++11 allowed lambda functions to deduce the return type based on the type of the expression given to the return statement. C++14 provides this ability to all functions. It also extends these facilities to lambda functions, allowing return type deduction for functions that are not of the form return expression;.[3]

c++允许 lambda 表达式根据返回值表达式推测返回值类型, c++14将其范围提升到了所有函数.

(后面这句我不明白它是什么意思)

In order to induce return type deduction, the function must be declared with auto as the return type, but without the trailing return type specifier in C++11:

为了引进返回值推测, 需要在声明时在返回值类型上带上 auto 关键字. 而不需要尾随返回值说明符.

1
auto DeduceReturnType();   // Return type to be determined.

If multiple return expressions are used in the function’s implementation, then they must all deduce the same type.[4]

如果函数实现中存在多个返回值表达式, 它们必须被能被推测为一种类型.

Functions that deduce their return types can be forward declared, but they cannot be used until they have been defined. Their definitions must be available to the translation unit that uses them.

返回值类型推导可以前向声明. 但是直到被定义时才能使用. 定义必须能被翻译单元使用.

Recursion can be used with a function of this type, but the recursive call must happen after at least one return statement in the definition of the function:[4]

递归函数也可以使用返回值推导, 但是必须在至少一个返回语句之后使用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
auto Correct(int i)
{
if (i == 1)
return i; // return type deduced as int

return Correct(i-1)+i; // ok to call it now
}

auto Wrong(int i)
{
if (i != 1)
return Wrong(i-1)+i; // Too soon to call this. No prior return statement.

return i; // return type deduced as int
}

Alternate type deduction on declaration[5]

In C++11, two methods of type deduction were added. auto was a way to create a variable of the appropriate type, based on a given expression. decltype was a way to compute the type of a given expression. However, decltype and auto deduce types in different ways. In particular, auto always deduces a non-reference type, as though by using std::decay, while auto&& always deduces a reference type. However, decltype can be prodded into deducing a reference or non-reference type, based on the value category of the expression and the nature of the expression it is deducing:[3]

在 c++11, 引进了两种类型推导. autodecltype auto 总是推导出非引用类型, decltype 则推导出完整的类型.

1
2
3
4
5
6
7
8
int   i;
int&& f();
auto x3a = i; // decltype(x3a) is int
decltype(i) x3d = i; // decltype(x3d) is int
auto x4a = (i); // decltype(x4a) is int
decltype((i)) x4d = (i); // decltype(x4d) is int&
auto x5a = f(); // decltype(x5a) is int
decltype(f()) x5d = f(); // decltype(x5d) is int&&

C++14 adds the decltype(auto) syntax. This allows auto declarations to use the decltype rules on the given expression.

c++ 增加了 decltype(auto) 语法, 这使 auto 声明可以在指定表达式上使用 decltype 规则.

The decltype(auto) syntax can also be used with return type deduction, by using decltype(auto) syntax instead of auto for the function’s return type deduction.[4]

decltype(auto) 语法还可以在返回值类型推导上使用.

Relaxed constexpr restrictions[edit]

C++11 introduced the concept of a constexpr-declared function; a function which could be executed at compile time. Their return values could be consumed by operations that require constant expressions, such as an integer template argument. However, C++11 constexpr functions could only contain a single expression that is returned (as well as static_asserts and a small number of other declarations).

c++引进了常量声明函数(一个可以在编译期执行的函数). 他么的返回值可以被常量表达式使用. 然而, c++11 常量表达式函数只能包含单个返回的表达式

C++14 relaxes these restrictions. Constexpr-declared functions may now contain the following:[3]

c++14 取消了这些限制, 常量表达式函数可以包含以下:

  • Any declarations except:

    • static or thread_local variables.

      staicthread_local 变量.

    • Variable declarations without initializers.

      没有初始化的变量声明(???)

  • The conditional branching statements if and switch.

    if 和 switch 条件语句

  • Any looping statement, including range-based for.

    任何循环语句, 包括基于循环的 for

  • Expressions which change the value of an object if the lifetime of that object began within the constant expression function. This includes calls to any non-const constexpr-declared non-static member functions.

    可更改开始于常量表达式函数的对象的值. 这包括任何对非常属性, 常量表达式定义, 非静态成员函数的调用.

goto statements are forbidden in C++14 relaxed constexpr-declared functions.

Also, C++11 stated that all non-static member functions that were declared constexpr were also implicitly declared const, with respect to this. That has since been removed; non-static member functions may be non-const.[6] However, per the restrictions above, a non-const constexpr member function can only modify a class member if that object’s lifetime began within the constant expression evaluation.

C++11规定所有非静态, 被 constexpr 声明的成员函数是隐式声明为 const 的. 这同样也被移除了, 非静态成员函数可以是 non-const 的. 然而, 根据上面的限制, 一个 non-const constexpr 的成员函数只能更改对象生命周期随常量表达式求值开始的对象的成员(???)

(我在其他文档中并未看到具体的关于更改值的介绍, 我简单理解的话, constexpr 现已可以支持循环和分支语句, 并且可以包含 static 和 thread_local 变量)

Variable templates[edit]

In prior versions of C++, only functions, classes or type aliases could be templated. C++14 now allows the creation of variables that are templated. An example given in the proposal is a variable pi that can be read to get the value of pi for various types (e.g., 3 when read as an integral type; the closest value possible with float, double or long double precision when read as float, double or long double, respectively; etc.).

在之前版本的 C++ 中, 类/类型别名可以模板化, C++14 允许创建变量模板.

The usual rules of templates apply to such declarations and definitions, including specialization.[7][8]

1
2
3
4
5
6
template<typename T>
constexpr T pi = T(3.141592653589793238462643383);

// Usual specialization rules apply:
template<>
constexpr const char* pi<const char*> = "pi";

Aggregate member initialization[edit]

C++11 added member initializers, expressions to be applied to members at class scope if a constructor did not initialize the member itself. The definition of aggregates was changed to explicitly exclude any class with member initializers; therefore, they are not allowed to use aggregate initialization.

C++14 relaxes this restriction,[3] allowing aggregate initialization on such types. If the braced init list does not provide a value for that argument, the member initializer takes care of it.[9]

(我不明白说的什么, 成员初始化不是 C++11 就有的么?)

Binary literals[edit]

Numeric literals in C++14 can be specified in binary form.[3] The syntax uses the prefixes 0b or 0B. The syntax is also used in other languages e.g. Java, C#, Swift, Go, Scala, Ruby, Python, OCaml, and as an unofficial extension in some C compilers since at least 2007.[10]

Digit separators[edit]

In C++14, the single-quote character may be used arbitrarily as a digit separator in numeric literals, both integer literals and floating point literals.[11] This can make it easier for human readers to parse large numbers through subitizing.

auto integer_literal = 1’000’000;
auto floating_point_literal = 0.000’015’3;
auto binary_literal = 0b0100’1100’0110;
auto silly_example = 1’0’0’000’00;

C++14中, 单引号专用于整数/浮点数表示, 使人看起来可以更加清晰(woo, 真是个人性化的功能)

Generic lambdas[edit]

In C++11, lambda function parameters need to be declared with concrete types. C++14 relaxes this requirement, allowing lambda function parameters to be declared with the auto type specifier.[7]

在 C++11 中, lambda 函数参数声明必须具有具体的类型, C++14 放松了这个要求, 允许 lambda 函数参数使用 auto 关键字声明, 如下:

1
auto lambda = [](auto x, auto y) {return x + y;};

Concerning auto type deduction, generic lambdas follow the rules of template argument deduction (which are similar, but not identical in all respects[*clarification needed*]). The code above is equivalent to this:[12]

关于 auto 类型推导, 泛型 lambdas 遵循模板参数推导原则. 上述代码等同于以下:

(其实也证实了, 与其说是匿名函数, 不如说是带 () 重载的匿名类)

1
2
3
4
5
struct
{
template<typename T, typename U>
auto operator()(T x, U y) const {return x + y;}
} lambda{};

Generic lambdas are essentially templated functor lambdas.

泛型 lambdas 是更高效的函数模板

Lambda capture expressions[edit]

C++11 lambda functions capture variables declared in their outer scope by value-copy or by reference. This means that value members of a lambda cannot be move-only types.[13] C++14 allows captured members to be initialized with arbitrary expressions. This allows both capture by value-move and declaring arbitrary members of the lambda, without having a correspondingly named variable in an outer scope.[7]

C++11 在其所在作用域中按值/引用捕获变量, 这意味着 lambda 成员的值不能是 move-only 类型. C++14 循序被捕获变量以任意形式初始化. 这使所有捕获可以值/移动, 以及声明任意的 lambda 成员, 而不需要在外层作用域中有对应的已命名成员.

This is done via the use of an initializer expression:

1
auto lambda = [value = 1] {return value;};

The lambda function lambda returns 1, which is what value was initialized with. The declared capture deduces the type from the initializer expression as if by auto.

声明捕获像 auto 一样推导初始化表达式.

This can be used to capture by move, via the use of the standard std::move function:

还可以用于捕获移动语义(赞啊 -v- )

1
2
std::unique_ptr<int> ptr(new int(10));
auto lambda = [value = std::move(ptr)] {return *value;};

The attribute [[deprecated]][edit]

The deprecated attribute allows marking an entity deprecated, which makes it still legal to use but puts users on notice that use is discouraged and may cause a warning message to be printed during compilation. An optional string literal can appear as the argument of deprecated, to explain the rationale for deprecation and/or to suggest a replacement.

deprecated 属性可以标记一个整体为’废弃的’, 继续使用这个整体是合法的, 但是用户会在编译时收到一个警告.

deprecated 可以增加字符串文本, 用作警示语.

1
2
3
4
5
6
7
8
9
10
11
12
[[deprecated]] int f();

[[deprecated("g() is thread-unsafe. Use h() instead")]]
void g( int& x );

void h( int& x );

void test()
{
int a = f(); // warning: 'f' is deprecated
g(a); // warning: 'g' is deprecated: g() is thread-unsafe. Use h() instead
}

(在 gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516 版本下, 对于类的支持有所不足)

New standard library features[edit]

Shared mutexes and locking[edit]

C++14 adds a shared timed mutex and a companion shared lock type.[14][15]

C++14 增加了共享互斥锁, 以及他的’伴侣’共享锁类型.

(这里有点不好说, mutex 本身是一把锁, 而 lock 也是锁的意思, 不过是加锁, lock(mutex), emm, 应该是这意思)

Heterogeneous lookup in associative containers[edit]

The C++ Standard Library defines four associative container classes. These classes allow the user to look up a value based on a value of that type. The map containers allow the user to specify a key and a value, where lookup is done by key and returns a value. However, the lookup is always done by the specific key type, whether it is the key as in maps or the value itself as in sets.

C++ 标准库定义了四种关联的容器类, 这些类型使用户可以检查基于该值类型的值. 然而, 检查总是需要指定类型来完成 (??? 卧槽 你想干嘛???)

C++14 allows the lookup to be done via an arbitrary type, so long as the comparison operator can compare that type with the actual key type.[16] This would allow a map from std::string to some value to compare against a const char* or any other type for which an operator<overload is available. It is also useful for indexing composite objects in a std::set by the value of a single member without forcing the user of find to create a dummy object (for example creating an entire struct Person to find a person by name).

C++14 允许检查可以经由任意类型完成, 只要对比操作可以和正确的键类型对比. 这使 map<std::string>可以和 const char* 类型的值或其他有有效 < 重载的操作符的类型(为什么一定是 < ? > 它不香么?)

当要检查一个集合中的复合类型时, 不需要创建一个复杂的复合类型也可以检索 (比如: 检索 struct person, 可以使用他的名字, 而并不需要创建一个 person 对象)

To preserve backwards compatibility, heterogeneous lookup is only allowed when the comparator given to the associative container allows it. The standard library classes std::less<> and std::greater<> are augmented to allow heterogeneous lookup.[17]

为了保持向后兼容性, heterogeneous 检查只在关联的容器允许对比器时才适用. 标准库 std::less<>, std::greater<> 被 heterogeneous 检查接纳 (诶, 我不知道这该怎么写…)

(这是我在网上找到的代码, 其中两个包含 thread::id 的重载缺一不可, 也就是说编译器都会用到)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct ThreadCmp {
using is_transparent = void;
// Regular overload.
bool operator()(const std::thread& a, const std::thread& b) const {
return a.get_id() < b.get_id();
}
// Transparent overloads
bool operator()(const std::thread& a, std::thread::id b) const {
return a.get_id() < b;
}
bool operator()(std::thread::id a, const std::thread& b) const {
return a < b.get_id();
}
//bool operator()(std::thread::id a, std::thread::id b) const {
// return a < b;
//}
};

int main() {
std::set<std::thread, ThreadCmp> threads;
// Can't construct an instance of `std::thread` with the same id, just to do the lookup.
// But we can look up by id instead.
std::thread::id id = this_thread::get_id();;
auto it = threads.find(id);
}

Standard user-defined literals[edit]

C++11 defined the syntax for user-defined literal suffixes, but the standard library did not use any of them. C++14 adds the following standard literals:[16]

C++14 增加了以下标准字面量

  • “s”, for creating the various std::basic_string types.

    “s”, 创建各种 std::basic_string 类型

  • “h”, “min”, “s”, “ms”, “us”, “ns”, for creating the corresponding std::chrono::duration time intervals.

    “h”, “min”, “s”, “ms”, “us”, “ns”, 创建对应的 std::chrono::duration 时间间隔

  • “if”, “i”, “il”, for creating the corresponding std::complex, std::complex and std::complex imaginary numbers.

1
2
3
auto str = "hello world"s; // auto deduces string
auto dur = 60s; // auto deduces chrono::seconds
auto z = 1i; // auto deduces complex<double>

The two “s” literals do not interact, as the string one only operates on string literals, and the one for seconds operates only on numbers.[18]

Tuple addressing via type[edit]

The std::tuple type introduced in C++11 allows an aggregate of typed values to be indexed by a compile-time constant integer. C++14 extends this to allow fetching from a tuple by type instead of by index.[16] If the tuple has more than one element of the type, a compile-time error results:[19]

C++14 扩展了 std::tuple , 可以通过类型来获取元素, 如果 tuple 有多个相同类型, 则会出错.

1
2
3
4
tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7
int j = get<2>(t); // Same as before: j == 7
string s = get<string>(t); // Compile-time error due to ambiguity

Smaller library features[edit]

std::make_unique can be used like std::make_shared for std::unique_ptr objects.[7]

std::integral_constant gained an operator() overload to return the constant value.[16]

The class template std::integer_sequence and related alias templates were added for representing compile-time integer sequences, such as the indices of elements in a parameter pack.[20]

The global std::begin/std::end functions were augmented with std::cbegin/std::cend functions, which return constant iterators, and std::rbegin/std::rend and std::crbegin/std::crend which return reverse iterators.

The std::exchange function template assigns a new value to a variable and returns the old value.[21]

New overloads of std::equal, std::mismatch, and std::is_permutation take a pair of iterators for the second range, so that the caller does not need to separately check that the two ranges are of the same length.[22]

The std::is_final type trait detects if a class is marked final.

The std::quoted stream I/O manipulator allows inserting and extracting strings with embedded spaces, by placing delimiters (defaulting to double-quotes) on output and stripping them on input, and escaping any embedded delimiters.[23]

summary

  1. 类型推导, type deduce

    auto 为主, 可以推导返回值而不需返回值后置语法. 同时 decltype 可以用于推导 auto 的原类型

  2. 常量表达式加强 constexpr

    constexpr 中可以有静态变量, thread_local 变量, 可以有分支和循环语句

  3. 值模板 variable template

    不仅类, 现在值也可以模板化

  4. 数字分隔符 digit separators

    使数字更加易读

  5. 加强 lambda

    lambda 的参数可以是 auto 推导的了, 同时 lambda 的捕获增加了移动语义, 还可以不是外部成员(也就是自建变量)

  6. 关键字 [[deprecated]]

    用于警示用户

  7. 新的字面量, “s”, “h”, “min” …

    新的字面量, 用于快捷创建变量, 而不需显式转换类型

  8. 新的 tuple 元素索引方式

    可以用类型来检索了呢 :)

以上是我从 C++14 中印象比较深刻的新特性

these are my impressive new features in c++14