文章目录
函数返回值类型推导
#include <iostream>
using namespace std;
// C++11不支持
template<typename T>
auto func(T t) {
return t;
}
int main() {
cout << func(4) << endl;
cout << func(3.4) << endl;
return 0;
}
- 如果有多个return语句,它们必须返回相同的类型,否则编译失败
auto func(bool flag) {
if (flag) return 1;
else return 2.3; // error
}
// inconsistent deduction for auto return type: ‘int’ and then ‘double’
- 如果return语句返回初始化列表,返回值类型推导也会失败
auto func() {
return {1, 2, 3}; // error returning initializer list
}
- 如果函数是虚函数,不能使用返回值类型推导
struct A {
// error: virtual function cannot have deduced return type
virtual auto func() { return 1; }
}
- 返回类型推导可以用在前向声明中,但是在使用它们之前,翻译单元中必须能够得到函数定义
auto f(); // declared, not yet defined
auto f() { return 42; } // defined, return type is int
int main() {
cout << f() << endl;
}
- 返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型
auto sum(int i) {
if (i == 1)
return i; // return int
else
return sum(i - 1) + i; // ok
}
lambda参数auto
在C++14,lambda表达式参数可以直接是auto:
auto f = [] (auto a) { return a; };
cout << f(1) << endl;
cout << f(2.3f) << endl;
变量模板
template<class T>
constexpr T pi = T(3.1415926535897932385L);
int main() {
cout << pi<int> << endl; // 3
cout << pi<double> << endl; // 3.14159
return 0;
}
别名模板
template<typename T, typename U>
struct A {
T t;
U u;
};
template<typename T>
using B = A<T, int>;
int main() {
B<double> b;
b.t = 10;
b.u = 20;
cout << b.t << endl;
cout << b.u << endl;
return 0;
}
constexpr的限制
C++11中constexpr函数必须必须把所有东西都放在一个单独的return语句中
// C++14 和 C++11均可
constexpr int factorial(int n) {
return n <= 1 ? 1 : (n * factorial(n - 1));
}
// C++11中不可,C++14中可以
constexpr int factorial(int n) {
int ret = 0;
for (int i = 0; i < n; ++i) {
ret += i;
}
return ret;
}
[[deprecated]]标记
当程序中使用到了被其修饰的代码时,编译时被产生警告,用户提示开发者该标记修饰的内容将来可能会被丢弃,尽量不要使用
struct [[deprecated]] A { };
int main() {
A a;
return 0;
}
出现警告:
~/test$ g++ test.cc -std=c++14
test.cc: In function ‘int main()’:
test.cc:11:7: warning: ‘A’ is deprecated [-Wdeprecated-declarations]
A a;
^
test.cc:6:23: note: declared here
struct [[deprecated]] A {
二进制字面量与整形字面量分隔符
C++14引入了二进制字面量,也引入了分隔符,防止看起来眼花
可以使用 0b 或者 0B 前缀表示一个二进制字面量
int a = 0b0001'0011'1010;
double b = 3.14'1234'1234'1234;
std::make_unique
C++11中有std::make_shared,却没有std::make_unique,在C++14已经改善
struct A {};
std::unique_ptr<A> ptr = std::make_unique<A>();
std::shared_timed_mutex与std::shared_lock
C++14通过std::shared_timed_mutex和std::shared_lock来实现读写锁,保证多个线程可以同时读,但是写线程必须独立运行,写操作不可以同时和读操作一起进行
std::lock_guard用于管理std::mutex
std::unique_lock与std::shared_lock管理std::shared_mutex
std::shared_timed_mutex
限时读写锁
特殊接口如下:
template< class Rep, class Period >
bool try_lock_shared_for( const std::chrono::duration<Rep,Period>& timeout_duration );
template< class Clock, class Duration >
bool try_lock_shared_until( const std::chrono::time_point<Clock,Duration>& timeout_time );
struct ThreadSafe {
mutable std::shared_timed_mutex mutex_;
int value_;
ThreadSafe() {
value_ = 0;
}
int get() const {
std::shared_lock<std::shared_timed_mutex> loc(mutex_);
return value_;
}
void increase() {
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
value_ += 1;
}
};
std::integer_sequence
作为模板元编程中的一员,主要用于生成编译时的整数序列。它本身并不存储任何数据,而是代表了一种类型,这种类型描述了一系列整数
这个模板类在 头文件中定义
特性概览:
- 类型安全性(Type-Safety)
std::integer_sequence 通过模板和类型参数化提供了编译时的类型检查,确保类型的一致性和安全性。
- 编译时确定(Compile-Time Evaluation)
序列中的整数在编译时就被确定,这意味着它们可以用于编译时的决策和计算,从而优化运行时性能。
- 类型表达(Type Representation)
它不直接存储数值,而是通过类型表达了一个整数序列。这一点体现了编程中“类型即文档(Types as Documentation)”的理念,使代码更加清晰和有表达力。
- 灵活性与扩展性(Flexibility and Extensibility)
作为模板,它可以与其他模板代码无缝集成,为复杂的模板元编程提供了强大的基础。
std::interger_sequence
template <class _Ty, _Ty... _Vals>
struct integer_sequence { // sequence of integer parameters
static_assert(is_integral_v<_Ty>, "integer_sequence<T, I...> requires T to be an integral type.");
using value_type = _Ty;
_NODISCARD static constexpr size_t size() noexcept {
return sizeof...(_Vals);
}
};
通过static_assert和std::is_integral_v限制数据的类型为integral, 通过constexpr编译时求序列的大小。
std::index_sequence
template <size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;
它是一个类模板,表示数据类型为std::size_t的一个序列
std::make_index_sequence
template <class _Ty, _Ty _Size>
using make_integer_sequence = __make_integer_seq<integer_sequence, _Ty, _Size>;
std::make_index_sequence是一个模板别名,它生成一个std::index_sequence类型的对象,该对象包含一系列递增的整数。这个工具在编译时生成一系列的索引,常常用于元编程和编译时计算。
template <size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;
std::size_t _size是一个模板参数,表示生成的序列的大小。std::make_integer_sequence是一个模板,它生成一个包含从0到N-1的整数序列的类型。
template <class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;
通常使用std::make_index_sequence来生成一个索引序列,然后使用这个序列来访问元组或数组的元素。这样,我们可以在编译时生成代码,而不需要在运行时进行循环。
示例 1:打印序列的值
#include <array>
#include <iostream>
#include <tuple>
#include <utility>
template <typename T, T... ints>
void print_sequence(std::integer_sequence<T, ints...> int_seq) {
std::cout << "The sequence of size " << int_seq.size() << ": ";
((std::cout << ints << ' '), ...);
std::cout << '\n';
}
// 转换数组为 tuple
template <typename Array, std::size_t... I>
auto a2t_impl(const Array &a, std::index_sequence<I...>) {
return std::make_tuple(a[I]...);
}
template <typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
auto a2t(const std::array<T, N> &a) {
return a2t_impl(a, Indices{});
}
// 漂亮地打印 tuple
template <class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch, Tr> &os, const Tuple &t, std::index_sequence<Is...>) {
((os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
}
template <class Ch, class Tr, class... Args>
auto &operator<<(std::basic_ostream<Ch, Tr> &os, const std::tuple<Args...> &t) {
os << "(";
print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
return os << ")";
}
int main() {
// The sequence of size 7: 9 2 5 1 9 1 6
print_sequence(std::integer_sequence<unsigned, 9, 2, 5, 1, 9, 1, 6>{});
// The sequence of size 20: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
print_sequence(std::make_integer_sequence<int, 20>{});
// The sequence of size 10: 0 1 2 3 4 5 6 7 8 9
print_sequence(std::make_index_sequence<10>{});
// The sequence of size 3: 0 1 2
print_sequence(std::index_sequence_for<float, std::iostream, char>{});
std::array<int, 4> array = {1, 2, 3, 4};
// 转换 array 为 tuple
auto tuple = a2t(array);
static_assert(std::is_same<decltype(tuple), std::tuple<int, int, int, int>>::value, "");
// 打印到 cout
std::cout << tuple << '\n'; // (1, 2, 3, 4)
}
示例 2:使用序列作为函数参数
#include <utility>
#include <iostream>
template<typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>) {
((std::cout << Ints << ' '), ...);
}
int main() {
// 创建一个包含 0, 1, 2, 3, 4 的序列
auto seq = std::make_integer_sequence<int, 5>{};
// 打印序列中的所有数字
print_sequence(seq);
}
在编译时对整数序列进行复杂的操作
示例 3:使用序列进行函数参数解包
#include <utility>
#include <iostream>
// 打印函数,用于输出一个参数
template<typename T>
void print(T t) {
std::cout << t << ' ';
}
// 递归终止函数
void print() {}
// 展开并打印参数包中的每个参数
template<typename T, typename... Args>
void print(T t, Args... args) {
print(t);
print(args...);
}
// 主函数,接受任意数量的参数,并打印它们
template<typename... Args, std::size_t... Is>
void printArgs(std::index_sequence<Is...>, Args... args) {
print(args...);
std::cout << std::endl;
}
int main() {
printArgs(std::index_sequence_for<int, double, std::string>(), 42, 3.14, "Hello, World!");
return 0;
}
利用 std::index_sequence_for 生成了一个与参数包 args 等长的序列。之后,通过参数包展开和序列,我们能够逐一处理每个参数
示例 4:编译时排序
可以在编译时对常量数据进行排序,从而提高运行时性能。
#include <utility>
#include <iostream>
#include <type_traits>
template<int... Ints>
using IntSequence = std::integer_sequence<int, Ints...>;
template<typename Seq, int N>
struct PushFront;
template<int... Ints, int N>
struct PushFront<IntSequence<Ints...>, N> {
using type = IntSequence<N, Ints...>;
};
template<typename Seq1, typename Seq2>
struct Concat;
template<int... Ints1, int... Ints2>
struct Concat<IntSequence<Ints1...>, IntSequence<Ints2...>> {
using type = IntSequence<Ints1..., Ints2...>;
};
template<typename Seq>
struct BubbleSort;
template<>
struct BubbleSort<IntSequence<>> {
using type = IntSequence<>;
};
template<int First, int... Rest>
struct BubbleSort<IntSequence<First, Rest...>> {
private:
using Next = typename BubbleSort<IntSequence<Rest...>>::type;
static constexpr bool PushFrontFirst = (First <= Next::Head) || std::is_same<Next, IntSequence<>>::value;
public:
using type = typename std::conditional<PushFrontFirst,
typename PushFront<Next, First>::type,
typename Concat<IntSequence<First>, typename PushFront<Next, Rest>::type>::type>::type;
};
int main() {
using Unsorted = IntSequence<3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5>;
using Sorted = BubbleSort<Unsorted>::type;
// 输出排序结果
std::cout << Sorted::size() << std::endl; // 输出排序后序列的大小
return 0;
}
通过递归模板元编程和 std::integer_sequence 来在编译时对整数序列进行排序
std::integer_sequence和std::tuple的配合使用
template <std::size_t... Is, typename F, typename T>
auto map_filter_tuple(F f, T& t) {
return std::make_tuple(f(std::get<Is>(t))...);
}
template <std::size_t... Is, typename F, typename T>
auto map_filter_tuple(std::index_sequence<Is...>, F f, T& t) {
return std::make_tuple(f(std::get<Is>(t))...);
}
template <typename S, typename F, typename T>
auto map_filter_tuple(F&& f, T& t) {
return map_filter_tuple(S{}, std::forward<F>(f), t);
}
#include <array>
#include <iostream>
#include <tuple>
#include <utility>
template <typename T, T... ints>
void print_sequence(std::integer_sequence<T, ints...> int_seq) {
std::cout << "The sequence of size " << int_seq.size() << ": ";
((std::cout << ints << ' '), ...);
std::cout << '\n';
}
// 转换数组为 tuple
template <typename Array, std::size_t... I>
auto a2t_impl(const Array &a, std::index_sequence<I...>) {
return std::make_tuple(a[I]...);
}
template <typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
auto a2t(const std::array<T, N> &a) {
return a2t_impl(a, Indices{});
}
// 漂亮地打印 tuple
template <class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch, Tr> &os, const Tuple &t, std::index_sequence<Is...>) {
((os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), ...);
}
template <class Ch, class Tr, class... Args>
auto &operator<<(std::basic_ostream<Ch, Tr> &os, const std::tuple<Args...> &t) {
os << "(";
print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
return os << ")";
}
int main() {
print_sequence(std::integer_sequence<unsigned, 9, 2, 5, 1, 9, 1, 6>{});
print_sequence(std::make_integer_sequence<int, 20>{});
print_sequence(std::make_index_sequence<10>{});
print_sequence(std::index_sequence_for<float, std::iostream, char>{});
std::array<int, 4> array = {1, 2, 3, 4};
// 转换 array 为 tuple
auto tuple = a2t(array);
static_assert(std::is_same<decltype(tuple), std::tuple<int, int, int, int>>::value, "");
// 打印到 cout
std::cout << tuple << '\n';
}
The sequence of size 7: 9 2 5 1 9 1 6
The sequence of size 20: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
The sequence of size 10: 0 1 2 3 4 5 6 7 8 9
The sequence of size 3: 0 1 2
(1, 2, 3, 4)
std::exchange
int main() {
std::vector<int> v;
std::exchange(v, {1,2,3,4});
cout << v.size() << endl;
for (int a : v) {
cout << a << " ";
}
return 0;
}
exchange的实现
template<class T, class U = T>
constexpr T exchange(T& obj, U&& new_value) {
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
new_value的值给了obj,而没有对new_value赋值
std::quoted
用于给字符串添加双引号
int main() {
string str = "hello world";
cout << str << endl;
cout << std::quoted(str) << endl;
return 0;
}
编译&输出
hello world
"hello world"