C++11/14/17常用特性
关键字
auto
让编译器根据上下文情况,确定auto变量的真正类型,可以作为函数的返回值。
std::map<uint64_t, std::vector<int>> mp;
// old
std::map<uint64_t, std::vector<int>>::iterator iter = mp.begin();
// new
auto iter = mp.begin();
// 修饰引用
int a = 3;
auto& b = a;
const auto& c = a;
// 用于需要迭代的场景
for (auto& iter : mp) {}
std::vector<int> vec;
std::sort(vec.begin(), vec.end(), [](const auto& x, const auto& y) {
return x < y;
});
// 用于函数返回值
auto addOne(int x) {
return x + 1;
}
auto addOne(int x) -> int {
return x + 1;
}
final
C++11中允许将类标记为final,直接在写方法的类名之后使用关键字final,表示此类不能被继承,如果误继承此类,编译器会报错。也可以用来修饰虚函数,表示此虚函数不能被重写。
// 修饰类
class A final {};
class B : public A {}; // compile error
// 修饰虚函数,不能修饰一般函数
class A {
public:
virtual void func() final {}
};
class B : public A {
public:
void func() override {} // compile error
};
override
如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法编译通过。
class A {
public:
virtual void funcA() {}
virtual void funcB() const {}
void funcC() {}
};
class B : public A {
public:
void funcA() override {}
void funcB() override {} // error, void funcB() const override {}
void funcC() override {} // error, funcC is not virtual function
};
default / delete
● 将函数声明为=default会显示地告诉编译器要为该类生成默认的函数
● 将函数声明为=delete将告诉编译器禁用该函数
class A {
public:
explicit A() = default;
explicit A(int i) : i(i) {}
A(const A& other) = delete;
void* operator new(size_t) = delete;
private:
int i;
};
int main() {
A a1;
A a2(2);
A a3(a2); // compile error
A* a4 = new A(); // compile error
return 0;
}
explicit
禁止隐式类型转换
class A {
public:
A(int i) : i(i) {}
private:
int i;
};
void func(const A& a) {
// ...
}
int main() {
func(1); // 发生隐式转换
return 0;
}
class A {
public:
explicit A(int i) : i(i) {}
private:
int i;
};
void func(const A& a) {
// ...
}
int main() {
func(1); // compile error
return 0;
}
using
C++11新增类似于typedef的功能,用于给类型命名。比typedef多的功能是可以定义带有模板的别名。
using Score = size_t;
template <typename T1, typename T2>
class B{};
template <typename T>
using FloatB = B<T, float>;
template <typename T>
FloatB<T> func(const Score& score) {
// ...
}
template <typename T>
typedef B<T, float> FloatB2; // compile error, typedef 不能定义带有模板的别名
decltype
类型推导
struct T {
int a;
} ;
int main() {
decltype(3) a;
decltype(a) b;
T t;
decltype(t.a) c;
return 0;
}
nullptr / nullptr_t
nullptr是专门一个表示空的值, 而nullptr_t是nullptr的类型
class A {};
void func(A* a) {
std::cout << "1";
}
void func(long long i) {
std::cout << "2";
}
int main() {
func(NULL); // 2
func(nullptr); // 1
decltype(NULL) a; // long long
decltype(nullptr) d; // nullptr_t
return 0;
}
constexpr
编译器常量表达式,可作用于函数返回值,函数参数,数据申明以及类的构造函数,条件表达式。
constexpr int a = 10;
constexpr int b = 20;
// 编译期的斐波那契数列
constexpr int func(int i) {
if (i <= 1) {
return i;
} else if (i == 2) {
return 1;
} else {
return func(i - 1) + func(i - 2);
}
}
int main() {
auto t1 = func(a);
auto t2 = func(b);
cout << t1 << endl;
cout << t2 << endl;
return 0;
}
// 用于条件表达式 if constexpr() {}
template <typename T>
std::enable_if_t<std::is_integral_v<T>, void> func(T a) {
if (a == 0) {
// ...
} else if (a <= std::numeric_limits<int8_t>::max() && a >= std::numeric_limits<int8_t>::min()) {
// ...
} else if (a <= std::numeric_limits<int16_t>::max() && a >= std::numeric_limits<int16_t>::min()) {
// ...
} else if constexpr(std::is_same_v<T, int32_t>) {
// ...
} else {
// ...
}
}
mutable
让变量在一个const函数中也可以被修改。
class A {
public:
[[nodiscard]] int get() const {
times++; // error
return i;
}
private:
int i {0};
int times {0};
};
// 正确做法
class A {
public:
[[nodiscard]] int get() const {
times++;
return i;
}
private:
int i {0};
mutable int times {0};
};
模板
通用
template <typename T1, typename T2>
void add(T1 t1, T2 t2) {
cout << "result: " << t1 + t2 << endl;
}
template <typename T, int size, auto cmpFunc>
class A {
public:
void sort() {
std::sort(vec.begin(), vec.end(), cmpFunc);
}
private:
std::vector<T> vec;
std::array<T, size> arr{};
};
bool cmpFunc(const int& a, const int& b) {
return a < b;
}
int main() {
// 调用模板函数
add(1, 2); // 3
add(std::string("123"), std::string("456")); // "123456"
// 调用模板类
A<int, 10, cmpFunc> a;
return 0;
}
可变长模板
模板数量可变的模板
template <typename T>
void print(T t) {
cout << "value: " << t << endl;
}
template <typename T, typename ...Types>
void print(T head, Types ...tail) {
cout << "value: " << head << endl;
print(tail...);
}
int main() {
print("1", 2, "abc", 1234, 3.01);
return 0;
}
// 输出
value: 1
value: 2
value: abc
value: 1234
value: 3.01
sizeof...
sizeof...可以求出可变长模板参数的个数。
template <typename T>
void print(T t) {
cout << "value: " << t << endl;
}
template <typename T, typename ...Types>
void print(T head, Types ...tail) {
cout << "tail size: " << sizeof...(tail);
cout << " value: " << head << endl;
print(tail...);
}
int main() {
print("1", 2, "abc", 1234, 3.01);
return 0;
}
// 输出
size: 5 value: 1
size: 4 value: 2
size: 3 value: abc
size: 2 value: 1234
value: 3.01
折叠表达式
// 二元
template <typename... T>
int add(T... value) {
return (value + ... + 3);
}
int main() {
auto res = add(1, 2, 3, 4, 5);
cout << res << endl; // 18
return 0;
}
// 一元
template <typename T>
void do_print(T value) {
cout << value << endl;
}
template <typename... T>
void print(T... value) {
(do_print(value), ...);
(..., do_print(value));
}
int main() {
print(1, 2, "3", "abc");
return 0;
}
// 输出
1
2
3
abc
1
2
3
abc
模板的模板参数
template <typename T>
using TransFunc = std::function<T(const std::string&)>;
template <typename T, template<typename> typename Algorithm>
std::enable_if_t<std::is_arithmetic_v<T>, TransFunc<T>> calculate(T a, T b) {
auto func = Algorithm<T>();
return [a, b, func](const std::string& str) {
T x = std::stoul(str);
return func(b, func(x, a));
};
}
template <typename T>
struct MyMinus {
T operator()(const T& a, const T& b) const {
return a + 3 - b;
}
};
template<>
struct MyMinus<double> {
double operator()(const double& a, const double& b) const {
return a * 2 - b;
}
};
int main() {
auto func1 = calculate<int, std::minus>(3, 4);
auto res1 = func1("3");
cout << "res: " << res1 << endl;
auto func2 = calculate<double, MyMinus>(3.0, 4.0);
auto res2 = func2("4");
cout << "res: " << res2 << endl;
}
type_tratis
template <typename T, T v>
struct integral_constant {
static constexpr T value = v;
using value_type = T;
using type = integral_constant<T, v>;
constexpr operator value_type() const noexcept {
return value;
}
constexpr value_type operator()() const noexcept {
return value;
}
};
template <bool B>
using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
template <typename T>
struct is_void : false_type {};
template <>
struct is_void<void> : true_type {};
template <>
struct is_void<void const> : true_type {};
template <>
struct is_void<void volatile> : true_type {};
template <>
struct is_void<void const volatile> : true_type {};
template <typename T>
inline constexpr bool is_void_v = is_void<T>::value;
函数式编程
让函数成为第一类型,函数像其他对象一样可以进行传递。
lambda表达式
利用lambda表达式可以方便的定义和创建匿名函数。
格式
[捕获列表](参数列表) -> 返回类型(可省略) {
函数体
}
捕获
● 按值捕获:将原值进行了拷贝保存到lambda,在函数体内不可以被改变。如果想要改变这个值,可以加上mutable关键字,但是只会改变这份拷贝,不影响被捕获的那个原始的值。
● 按引用捕获:捕获值的引用,对该值修改会同时修改被捕获的原始的值。坑:不要将捕获局部变量的lambda传递到其他函数中使用,要考虑局部变量的生命周期。
int main() {
int a = 3;
int b = 4;
auto func = [a, &b]() -> int {
b = 7;
// a = 4; compile error
return a + b;
};
std::cout << func() << std::endl; // 10
std::cout << a << std::endl; // 3
std::cout << b << std::endl; // 7
return 0;
}
std::function
std::function是一种通用的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、lambda表达式、函数指针、以及其他函数对象等。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹。
int add(int a) {
return a + 1;
}
typedef int (*AddFunc)(int);
AddFunc addFunc = add;
struct AddClass {
int operator()(int a) {
return a + 1;
}
};
int main() {
std::function func1 = add;
std::function func2 = addFunc;
std::function func3 = AddClass();
std::function func4 = [](int a) {
return a + 1;
};
std::function<int(int)> func5 = add; // 显示指定模板
std::function<int(int)> func6 = addFunc;
std::cout << func1(3) << std::endl; // 4
std::cout << func2(3) << std::endl; // 4
std::cout << func3(3) << std::endl; // 4
std::cout << func4(3) << std::endl; // 4
std::cout << func5(3) << std::endl; // 4
std::cout << func6(3) << std::endl; // 4
return 0;
}
std::bind
可以绑定函数和参数或部分参数生成一个函数对象。
int add(int a, int b) {
return a + b;
}
struct AddClass {
int operator()(int a, int b) {
return a + b;
}
};
int main() {
auto func1 = std::bind(add, std::placeholders::_1, 3);
auto func2 = [](int b) {
return add(3, b);
};
AddClass addClass;
auto func3 = std::bind(&AddClass::operator(), &addClass, std::placeholders::_1, 3);
auto func4 = [addClass](int b) mutable {
return addClass(3, b);
};
std::cout << func1(4) << std::endl;
std::cout << func2(4) << std::endl;
std::cout << func3(4) << std::endl;
std::cout << func4(4) << std::endl;
return 0;
}
// 输出
7
7
7
7
一些函数式的库
std::transform
std::vector<int> vec {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<std::pair<int, char>> result;
std::transform(vec.begin(), vec.end(), std::back_inserter(result), [](const auto& val) {
return std::pair{val, static_cast<char>(val - 1 + 'a')};
});
for (auto & p : result) {
std::cout << p.first << " " << p.second << std::endl;
}
// 输出
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
10 j
std::for_each / std::for_each_n
// std::for_each 输出同上
std::for_each(result.begin(), result.end(), [](const auto& p) {
std::cout << p.first << " " << p.second << std::endl;
});
// std::for_each_n
std::for_each_n(result.begin(), 3, [](const auto& p) {
std::cout << p.first << " " << p.second << std::endl;
});
// 输出
1 a
2 b
3 c
std::any_of / std::all_of / std::none_of
std::vector<int> vec {1, 1, 1, 3, 1, 1, 1};
auto res1 = std::any_of(vec.begin(), vec.end(), [](const auto& val) {
return val == 3;
}); // true
auto res2 = std::all_of(vec.begin(), vec.end(), [](const auto& val) {
return val == 1;
}); // false
auto res3 = std::none_of(vec.begin(), vec.end(), [](const auto& val) {
return val == 3;
}); // false
std::count / std::count_if
std::vector<int> vec {1, 1, 1, 3, 1, 1, 1, 4};
auto num1 = std::count(vec.begin(), vec.end(), 1);
std::cout << num1 << std::endl; // 6
auto num2 = std::count_if(vec.begin(), vec.end(), [](const auto& val){
return val < 4;
});
std::cout << num2 << std::endl; // 7
网友评论