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






4.92/5 (34投票s)
最吸引我的(对我而言!)新的 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
数组)。 因此,它可以用于非可变的情况(如比较或子字符串)。
致谢
- Lambda 捕获 *this - http://www.bfilipek.com/2017/01/cpp17features.html#lambda-capture-of-this
- C++14 - https://en.wikipedia.org/wiki/C%2B%2B14
- C++17 - https://en.wikipedia.org/wiki/C%2B%2B17
- http://libcxx.llvm.org/cxx1z_status.html
- C++17 实现状态 - https://clang.llvm.net.cn/cxx_status.html#cxx17
- C++17 中的新特性是什么? - https://stackoverflow.com/questions/38060436/what-are-the-new-features-in-c17
历史
- 2018 年 1 月 3 日:首次发布,新年快乐!