65.9K
CodeProject 正在变化。 阅读更多。
Home

从 C++ 11 到 C++ 17:一个演练

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (34投票s)

2018 年 1 月 3 日

CPOL

3分钟阅读

viewsIcon

28209

最吸引我的(对我而言!)新的 C++ 特性

引言

这篇小文章包含了自 C++ 11 到 C++ 17 的 20 个吸引我的特性,所以我将它们呈现给您,并附带一些讨厌的评论。 阅读愉快! 这篇文章将不断更新。

背景

是的,假设您完全了解 C++ 11。 这不是一篇入门文章。

功能

新的和更新的字面量


// C++ 14 Binary literals
int a = 0b111; // a = 7

// C++ 14 Prefixes
auto str = "hello"s; // auto deduces string
auto dur = 60s;      // auto deduces chrono::seconds
auto z   = 1i;       // auto deduces complex<double>

+1,虽然我有点不想过度使用 auto

数字分隔符


// C++ 14
int a = 1'000'000; 

+1。 使读取大数字(尤其是 64 位指针)更容易。

变量模板


// C++ 14
template <typename T> T pi = (T)3.141592;
double x = sin(2.0*440.0*pi<double>*0.01); // specializes pi as double
float perimeter = 2.0f*pi<float>*radius;   // specializes pi as float

=0。 我还没有真正找到任何有用的东西。 模板化函数或类是可以的 - 你想在许多类型中使用它。 模板化变量意味着我实际上对不同的变量使用相同的名称 - 哎呦。 实际上,我在线找到的唯一例子是 pi 版本。

函数返回类型自动推导


// C++ 14
auto foo(int j,int x)
{
    return j + x; // "auto" -> "int";
}

auto boo();

auto tu() // better than tuple<int,int,int> tu()
{
    return make_tuple<int,int,int>(5,6,7);
}

int main()
{
    auto t = tu();    // t = tuple(5,6,7)
    int a = foo(5,6); // OK
    int b = boo();    // Error
}

+1。 主要用于像上面的元组这样的长类型。 如果有多个返回语句,它们都必须推导出相同的类型。 由于这是一个单遍,因此推导必须在需要时可用。 例如,int b = boo() 行将失败,因为 boo 尚未定义,即使该行暗示一个 int 值。 在另一个 TU 中定义函数,甚至在同一个 TU 中但在该代码下方定义函数也会失败。

相关 Lambda 表达式


// C++ 14
auto lambda = [](auto x, auto y) {return x + y;};

+1。 Auto 使您可以少输入。

命名空间嵌套


// C++ 11
namespace A 
{
    namespace B
    {
        namespace C
        {
        }
    }
}

// C++ 17
namespace A::B::C
{
}

+1。 显著减少输入。

结构体绑定


tuple<int,float,char> x() { return make_tuple<int,float,char>(5,5.2f,'c'); }

// C++ 11
int a;
float b;
char c;
std::tie(a,b,c) = x();

// C++ 17
auto [a,b,c] = x();

+1。 它也适用于结构体。 问题是您不能像旧的 tie 中那样使用 std::ignore

std::optional


// C++ 17
using namespace std;
optional<string> foo()
{
    if (something)
       return "Hello";
    return {};
}

int main()
{
    auto rv = foo();
    if (rv) 
    {
         // We have a value, rv.value();
    }
    rv.value_or("dada"); // Either the value, or "dada"
}

+1。 允许返回一个“空”返回值,同时提供一个带有 value_or 的 catchall 情况。 但是,这并不像预期的那样工作

// C++ 17
using namespace std; 
optional<int> foo(int x) 
{ 
if (x == 1)
   return 5; 
}

现在我仍然收到编译器警告,并且可能存在未定义的行为 std::optional。 当存在非返回路径时,应该暗示 return {}(是的,我已经提出了它,是的,他们都不同意我的观点。谁在乎。 :))

std::any


// C++ 17
using namespace std;
any foo()
{
    string h("hello");
    return any(h);
}

int main()
{
    try 
    {
        auto r = any_cast<string>(foo());
    }
    catch(...)
    {
    }
}

+1 我不太喜欢它,但我发现了一个不错的 技巧 与它。

std::variant


// C++ 11
union A
{
    int a;
    float b;
    char c;
};

A x;
x.a = 5; 
float b = x.b; 

// C++ 17
using namespace std;
variant<int,float,char> x;
x = 5;                    // now contains int
int i = std::get<int>(v); // i = 5;
std::get<float>(v);       // Throws

=0,std::variant 不是一个普通的 union,而是一个类型安全的 union,因为它会在你尝试访问非当前类型时抛出异常。 然而,旧类型 union 的重点是在没有类型的情况下共享内存(考虑旧的 x86 union ax { struct {char al,char ah}; short ax; })。 我不太确定一个有用的用例场景。

折叠表达式


// C++ 11 add numbers
template <typename T>
T add(T t) { return t; }

template <typename T, typename... Many>
T add(T t, Many... many) {
  return t + add(many...);
}

// C++ 17 fold expressions
template <typename T, typename ... Many>
T add(T t, Many... many)
{
   return (t + ... + many);
}

// ----
int main()
{
  auto r = add(1,2,3,4,5,6,7,8,9,10); // r = 55, 
                                      // but in C++ 17 method we only need one function.
}

+100。 没有评论。 这个想法是将省略号与其中一个运算符结合起来,以产生包的非逗号扩展。

#if has_include


// C++ 17
#if __has_include(<string>)
// Assume <string> has been included
#else
#endif

=0。 没关系,概括了旧的 #pragma once 和 #IFNDEF _STRING_H 之类的东西。

template <auto>


// C++ 17
template <auto value> void foo() { }
foo<3>();               // deduces int

+1,autos 无处不在。 :)

一些新的属性


// C++ 14
// [[deprecated("reason")]]

void [[deprecated("This function is not safe")]] foo ()
{
}


// C++ 17
// [[fallthrough]]
// Notifies the compiler that a lack of break is intentional

switch(a)
{
    case 1:
       test();          // Warning issued, did the programmer forget the break statement?
    case 2: 
       test2();
       [[fallthrough]]; // No warning, the lack of break was intentional
    case 3: 
       ////
}

// [[nodiscard]] 
// Issues a warning when a return value of a function is discarded

[[nodiscard]] int foo()
{
    return 1;
}

int main()
{
   foo();              // issues a warning
   int a = foo();      // not a warning now
}

// [[maybe_unused]]
int foo()
{
    [[maybe_unused]] int y;
    // No warning later if y is not used. Something like UNREFERENCED_PARAMETER() macro
}

+1,它们是旧的 pragma 的不错的标准化。

if 和 switch 在条件之前初始化


// C++ 11
int val = foo();
if (val == 1)
{
}

// C++ 17
if (int val = foo() ; val == 1)
{
}

+1,区别在于变量的作用域仅在 if 内部。

内联变量


// C++ 17
inline int x = 5;
​​​​​

+1000,现在你也可以在.h文件中包含变量而没有问题。 它可以根据需要包含多次,并且只定义一次。

从初始化列表自动推导


// C++ 11
std::initializer_list<int> x1 = { 1, 2 };

// C++ 17
auto x1 = { 1, 2 }; // automatically std::initializer_list<int>

+1。 再次不错的自动推导。

构造函数模板推导


// C++ 11
auto p = std::pair<double,int>(5.0,0);

// C++ 17
auto p = std::pair(5.0,0); // deduces double,int

+1000,早就该这样了。

异常说明是类型的一部分


// C++ 11
void (*p)() throw(int);       // a function pointer to a function that throws an int
void (**pp)() throw() = &p;   // a function pointer to a function that does not throw

+1。 在 C++ 17 中,上述是一个错误。 异常说明 throw(int) 现在是类型的一部分。

最后.....

string_view


// C++ 17
using namespace std;

string a = "hello there";
// a uses dynamic memory 

string_view largeStringView{a.c_str(), a.size()};

+1000。 string_view 为我们提供了 std::string 的所有优点,但在已经拥有的 string 上(无论是另一个 string 还是一个 char 数组)。 因此,它可以用于非可变的情况(如比较或子字符串)。

致谢

历史

  • 2018 年 1 月 3 日:首次发布,新年快乐!
© . All rights reserved.