当前位置: 首页 > news >正文

C++学习:六个月从基础到就业——C++11/14:列表初始化

C++学习:六个月从基础到就业——C++11/14:列表初始化

本文是我C++学习之旅系列的第四十三篇技术文章,也是第三阶段"现代C++特性"的第五篇,主要介绍C++11/14中的列表初始化特性。查看完整系列目录了解更多内容。

引言

在C++11之前,C++中的初始化语法存在不一致性和局限性,不同类型的对象需要使用不同的初始化语法。C++11引入了统一初始化语法(通常称为花括号初始化或列表初始化),极大地简化和统一了对象初始化过程。这一特性让代码更加一致、安全,并为容器和用户自定义类型提供了更直观的初始化方式。

本文将深入探讨列表初始化的语法、原理、应用场景以及C++14中的改进,帮助你全面掌握这一现代C++的重要特性。

目录

  • C++11/14:列表初始化
    • 引言
    • 目录
    • 统一初始化语法基础
      • 传统初始化方式的问题
      • 花括号初始化的基本语法
      • 花括号初始化的特点
    • std::initializer_list与列表初始化
      • initializer_list的基本概念
      • 自定义类的列表初始化
        • 聚合类型的列表初始化
        • 使用initializer_list构造函数
        • 为类添加多个列表初始化构造函数
    • 列表初始化的高级特性
      • 防止窄化转换
      • 拷贝列表初始化与直接列表初始化
      • 自动型别推导与列表初始化
    • 列表初始化的实际应用
      • 容器初始化
      • 函数返回值的列表初始化
      • 作为函数参数
      • 配合类成员使用
    • C++14中的列表初始化改进
      • auto与返回值推导
      • 通用lambda表达式
    • 列表初始化的最佳实践与常见陷阱
      • 最佳实践
      • 常见陷阱
    • 实际应用示例
      • 示例1:简化配置对象创建
      • 示例2:数据处理管线
      • 示例3:自定义JSON构建器
    • 总结

统一初始化语法基础

传统初始化方式的问题

在C++11之前,C++中存在多种不同的初始化语法,这导致了语言的不一致性和学习难度:

// 变量初始化
int a = 10;                    // 赋值初始化
int b(20);                     // 直接初始化// 数组初始化
int arr1[] = {1, 2, 3, 4, 5};  // 数组初始化语法// 结构体和类初始化
struct Point { int x, y; };
Point p1 = {1, 2};             // 聚合初始化
Point p2(1, 2);                // 构造函数初始化// 动态分配的数组初始化
int* pArr = new int[3];        // 无法在创建时初始化内容
pArr[0] = 1; pArr[1] = 2; pArr[2] = 3;

这些不同语法之间的不一致性使得代码难以维护,也增加了初学者的学习负担。

花括号初始化的基本语法

C++11引入的花括号初始化(也称为列表初始化)提供了一种统一的、适用于几乎所有场景的初始化语法:

#include <iostream>
#include <vector>
#include <map>
#include <string>struct Point {int x, y;
};class Rectangle {
public:Rectangle(int width, int height) : width_(width), height_(height) {}int width() const { return width_; }int height() const { return height_; }
private:int width_;int height_;
};int main() {// 基本类型初始化int a{10};double b{3.14};bool flag{true};// 使用空花括号表示零初始化int c{};  // 等价于 int c{0};// 数组初始化int arr[]{1, 2, 3, 4, 5};// 动态分配的数组初始化int* pArr = new int[3]{1, 2, 3};// POD类型的聚合初始化Point p{1, 2};// 使用构造函数的对象初始化Rectangle rect{3, 4};// 容器初始化std::vector<int> vec{1, 2, 3, 4, 5};std::map<std::string, int> ages{{"Alice", 25}, {"Bob", 30}, {"Charlie", 35}};// 嵌套列表初始化std::vector<std::vector<int>> matrix{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};// 输出结果std::cout << "a = " << a << std::endl;std::cout << "Rectangle: " << rect.width() << "x" << rect.height() << std::endl;std::cout << "Vector: ";for (int num : vec) {std::cout << num << " ";}std::cout << std::endl;std::cout << "Matrix:" << std::endl;for (const auto& row : matrix) {for (int num : row) {std::cout << num << " ";}std::cout << std::endl;}delete[] pArr;return 0;
}

花括号初始化的特点

列表初始化相比其他初始化方式具有以下特点:

  1. 统一性:几乎适用于所有C++类型的初始化
  2. 防止窄化转换:不允许可能导致数据丢失的类型转换
  3. 数组初始化:允许直接初始化动态分配的数组
  4. 集合类型友好:特别适合容器和聚合类型的初始化

std::initializer_list与列表初始化

initializer_list的基本概念

在支持列表初始化的底层,C++11引入了std::initializer_list<T>模板类,它表示一个常量数组的视图。当使用花括号创建列表时,编译器会自动构造一个std::initializer_list对象。

std::initializer_list具有以下特点:

  1. 轻量级:不拥有元素,只是提供对元素的访问
  2. 只读:不能修改列表中的元素
  3. 类似容器:提供了begin()end()size()方法
  4. 支持范围循环:可以使用范围for循环遍历

示例代码:

#include <iostream>
#include <initializer_list>
#include <string>// 接受initializer_list的函数
void printNumbers(std::initializer_list<int> numbers) {std::cout << "Numbers: ";for (int num : numbers) {std::cout << num << " ";}std::cout << std::endl;
}// 具有initializer_list构造函数的类
class DataContainer {
private:std::vector<int> data;public:// 使用initializer_list构造DataContainer(std::initializer_list<int> values) : data(values) {std::cout << "Created container with " << values.size() << " elements" << std::endl;}// 接受initializer_list的方法void addValues(std::initializer_list<int> values) {data.insert(data.end(), values.begin(), values.end());}void print() const {std::cout << "Container content: ";for (int value : data) {std::cout << value << " ";}std::cout << std::endl;}
};int main() {// 使用initializer_list参数调用函数printNumbers({1, 2, 3, 4, 5});// 创建显式initializer_list对象std::initializer_list<std::string> names = {"Alice", "Bob", "Charlie"};std::cout << "Names: ";for (const auto& name : names) {std::cout << name << " ";}std::cout << std::endl;// 使用initializer_list构造对象DataContainer container{10, 20, 30};container.print();// 使用initializer_list参数调用方法container.addValues({40, 50, 60});container.print();return 0;
}

自定义类的列表初始化

要让自定义类支持列表初始化,有两种主要方法:

  1. 聚合初始化:用于满足聚合条件的类或结构体
  2. initializer_list构造函数:用于接受任意数量的同类型值
聚合类型的列表初始化

聚合类型满足以下条件:

  • 无用户定义的构造函数
  • 无私有或受保护的非静态数据成员
  • 无基类
  • 无虚函数

聚合类型可以直接使用花括号初始化:

struct Point {int x, y;
};struct Rectangle {Point topLeft;Point bottomRight;
};int main() {// 聚合初始化Point p{10, 20};// 嵌套聚合初始化Rectangle rect{{0, 0}, {100, 100}};return 0;
}
使用initializer_list构造函数

非聚合类型可以通过提供接受std::initializer_list的构造函数来支持列表初始化:

#include <iostream>
#include <initializer_list>
#include <vector>
#include <string>class CustomVector {
private:std::vector<int> data;public:// 默认构造函数CustomVector() = default;// 从initializer_list构造CustomVector(std::initializer_list<int> values) : data(values) {}// 添加元素void add(int value) {data.push_back(value);}// 打印内容void print() const {std::cout << "CustomVector: ";for (int value : data) {std::cout << value << " ";}std::cout << std::endl;}// 返回大小size_t size() const {return data.size();}
};int main() {// 使用initializer_list构造函数CustomVector v1{1, 2, 3, 4, 5};v1.print();// 空列表初始化CustomVector v2{};v2.print();// 添加元素v2.add(10);v2.add(20);v2.print();return 0;
}
为类添加多个列表初始化构造函数

可以添加多个接受不同类型initializer_list的构造函数:

#include <iostream>
#include <initializer_list>
#include <string>
#include <vector>class MultiTypeContainer {
private:std::vector<int> integers;std::vector<std::string> strings;public:// initializer_list<int>构造函数MultiTypeContainer(std::initializer_list<int> ints) : integers(ints) {std::cout << "Constructed with integers" << std::endl;}// initializer_list<string>构造函数MultiTypeContainer(std::initializer_list<std::string> strs) : strings(strs) {std::cout << "Constructed with strings" << std::endl;}// 打印内容void print() const {if (!integers.empty()) {std::cout << "Integers: ";for (int value : integers) {std::cout << value << " ";}std::cout << std::endl;}if (!strings.empty()) {std::cout << "Strings: ";for (const auto& str : strings) {std::cout << str << " ";}std::cout << std::endl;}}
};int main() {// 使用int列表初始化MultiTypeContainer container1{1, 2, 3, 4, 5};container1.print();// 使用string列表初始化MultiTypeContainer container2{"hello", "world", "initializer", "list"};container2.print();return 0;
}

列表初始化的高级特性

防止窄化转换

列表初始化的一个重要安全特性是防止窄化转换(Narrowing Conversion)。窄化转换是指可能导致数据丢失或更改的隐式类型转换,例如从浮点数到整数、从大整数类型到小整数类型等。

使用花括号初始化时,如果发生窄化转换,编译器将产生错误:

#include <iostream>int main() {// 以下代码能够正常编译int a = 3.14;           // 允许从double到int的窄化int b(3.14);            // 允许从double到int的窄化// 以下代码在编译时会报错// int c{3.14};         // 错误:从double到int的窄化转换// char d{1000};        // 错误:从int到char的窄化转换// 以下代码不是窄化,因此正常double e{3};            // 正确:从int到double不是窄化int f{3};               // 正确:相同类型// 即使是稍大类型到小类型的转换也会报错short s = 32767;// int arr[2]{s, 40000}; // 错误:40000超出short范围std::cout << "Traditional initialization allows narrowing: a = " << a << std::endl;std::cout << "Direct initialization allows narrowing: b = " << b << std::endl;return 0;
}

这种防止窄化转换的特性增强了代码的安全性,可以在编译阶段捕获潜在的数据丢失问题。

拷贝列表初始化与直接列表初始化

C++11中的列表初始化有两种形式:

  1. 直接列表初始化T obj{arg1, arg2, ...};
  2. 拷贝列表初始化T obj = {arg1, arg2, ...};

这两种形式在大多数情况下行为相似,但存在一些细微差别:

#include <iostream>
#include <vector>class ExplicitConstructor {
public:// 带有explicit关键字的构造函数explicit ExplicitConstructor(int value) : value_(value) {std::cout << "Explicit constructor called with " << value << std::endl;}int getValue() const { return value_; }private:int value_;
};int main() {// 直接列表初始化ExplicitConstructor obj1{42};  // 正确:直接初始化可以使用explicit构造函数// 拷贝列表初始化// ExplicitConstructor obj2 = {42};  // 错误:拷贝初始化不能用explicit构造函数// 对于标准容器的区别std::vector<int> vec1{5};      // 直接创建了一个包含单个元素5的向量std::vector<int> vec2 = {5};   // 使用initializer_list创建向量std::cout << "vec1 size: " << vec1.size() << std::endl;  // 输出 1std::cout << "vec2 size: " << vec2.size() << std::endl;  // 输出 1// 可能导致混淆的例子std::vector<int> vec3{5, 6};   // 创建包含5和6两个元素的向量std::vector<int> vec4{5};      // 创建包含一个元素5的向量std::vector<int> vec5(5);      // 创建包含5个值为0的元素的向量std::cout << "vec3 size: " << vec3.size() << std::endl;  // 输出 2std::cout << "vec4 size: " << vec4.size() << std::endl;  // 输出 1std::cout << "vec5 size: " << vec5.size() << std::endl;  // 输出 5return 0;
}

自动型别推导与列表初始化

C++11中的auto关键字与列表初始化结合时,遵循特殊规则:

#include <iostream>
#include <vector>
#include <typeinfo>int main() {// auto与列表初始化auto a = {1, 2, 3};  // a的类型是std::initializer_list<int>// 验证类型std::cout << "Type of a: " << typeid(a).name() << std::endl;// 使用auto和列表初始化的典型应用for (auto x : {1, 2, 3, 4, 5}) {std::cout << x << " ";}std::cout << std::endl;// C++17中的变化:列表只有一个元素时auto b = {42};  // 在C++11/14中:std::initializer_list<int>// 下面这行在C++17之前会产生错误,C++17中合法// auto c{42};   // C++17: int,C++11/14: std::initializer_list<int>return 0;
}

在C++17之前,auto与列表初始化一起使用时总是创建std::initializer_list对象。C++17引入了一些变化,单元素的直接列表初始化不再创建initializer_list

列表初始化的实际应用

容器初始化

列表初始化最常见的应用是简化容器的初始化:

#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <string>int main() {// 向量的列表初始化std::vector<int> numbers{1, 2, 3, 4, 5};// 集合的列表初始化std::set<int> uniqueNumbers{3, 1, 4, 1, 5, 9, 2, 6, 5};// 映射的列表初始化std::map<std::string, int> ages{{"Alice", 25},{"Bob", 30},{"Charlie", 35}};// 嵌套容器的初始化std::vector<std::vector<int>> matrix{{1, 2, 3},{4, 5, 6},{7, 8, 9}};// 输出内容std::cout << "Numbers: ";for (int num : numbers) {std::cout << num << " ";}std::cout << std::endl;std::cout << "Unique numbers: ";for (int num : uniqueNumbers) {std::cout << num << " ";}std::cout << std::endl;std::cout << "Ages:" << std::endl;for (const auto& [name, age] : ages) {std::cout << name << ": " << age << std::endl;}std::cout << "Matrix:" << std::endl;for (const auto& row : matrix) {for (int value : row) {std::cout << value << " ";}std::cout << std::endl;}return 0;
}

函数返回值的列表初始化

列表初始化还可以用于简化函数返回值的创建:

#include <iostream>
#include <vector>
#include <utility>
#include <string>// 返回vector的函数
std::vector<int> getNumbers() {return {1, 2, 3, 4, 5};  // 返回初始化列表
}// 返回pair的函数
std::pair<std::string, int> getNameAndAge() {return {"Alice", 30};  // 返回初始化列表
}// 返回嵌套容器的函数
std::vector<std::pair<std::string, int>> getPeople() {return {{"Alice", 25},{"Bob", 30},{"Charlie", 35}};
}int main() {auto numbers = getNumbers();auto [name, age] = getNameAndAge();  // C++17结构化绑定auto people = getPeople();std::cout << "Numbers: ";for (int num : numbers) {std::cout << num << " ";}std::cout << std::endl;std::cout << "Name: " << name << ", Age: " << age << std::endl;std::cout << "People:" << std::endl;for (const auto& [person, personAge] : people) {std::cout << person << ": " << personAge << std::endl;}return 0;
}

作为函数参数

列表初始化可以直接在函数调用时使用:

#include <iostream>
#include <vector>
#include <algorithm>// 接受vector的函数
void processNumbers(const std::vector<int>& numbers) {std::cout << "Processing: ";for (int num : numbers) {std::cout << num << " ";}std::cout << std::endl;
}// 接受initializer_list的函数
template<typename T>
void printValues(std::initializer_list<T> values) {std::cout << "Values: ";for (const auto& value : values) {std::cout << value << " ";}std::cout << std::endl;
}int main() {// 函数调用时的列表初始化processNumbers({1, 2, 3, 4, 5});// 对模板函数的调用printValues({1, 2, 3, 4, 5});        // T推导为intprintValues({"a", "b", "c", "d"});   // T推导为const char*printValues({1.1, 2.2, 3.3});        // T推导为double// 与算法结合使用std::vector<int> vec{10, 20, 30};vec.insert(vec.begin(), {-3, -2, -1, 0});std::cout << "Vector after insertion: ";for (int num : vec) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

配合类成员使用

列表初始化可以用于类的非静态成员变量初始化:

#include <iostream>
#include <vector>
#include <string>class Person {
private:std::string name;int age;std::vector<std::string> hobbies;public:// 使用默认成员初始化器Person() : name("Unknown"), age(0), hobbies{} {}// 接受name和age的构造函数Person(std::string n, int a) : name(std::move(n)), age(a), hobbies{} {}// 接受所有字段的构造函数Person(std::string n, int a, std::initializer_list<std::string> h): name(std::move(n)), age(a), hobbies(h) {}// 打印信息void print() const {std::cout << "Name: " << name << ", Age: " << age << std::endl;if (!hobbies.empty()) {std::cout << "Hobbies: ";for (const auto& hobby : hobbies) {std::cout << hobby << " ";}std::cout << std::endl;}}
};// C++11允许非静态成员变量初始化
class Widget {
private:int id{0};                                  // 默认初始化为0std::string name{"Default"};                // 默认初始化为"Default"std::vector<int> data{1, 2, 3, 4, 5};       // 使用列表初始化public:// 构造函数可以重写成员初始化器Widget() = default;Widget(int i) : id(i) {}  // name和data使用默认初始化器void print() const {std::cout << "Widget ID: " << id << ", Name: " << name << std::endl;std::cout << "Data: ";for (int value : data) {std::cout << value << " ";}std::cout << std::endl;}
};int main() {// 使用不同构造函数创建Person对象Person p1;Person p2("Alice", 30);Person p3("Bob", 25, {"Reading", "Gaming", "Hiking"});std::cout << "Person 1:" << std::endl;p1.print();std::cout << "\nPerson 2:" << std::endl;p2.print();std::cout << "\nPerson 3:" << std::endl;p3.print();// 使用成员初始化器的Widgetstd::cout << "\nWidget 1:" << std::endl;Widget w1;w1.print();std::cout << "\nWidget 2:" << std::endl;Widget w2(42);w2.print();return 0;
}

C++14中的列表初始化改进

C++14在列表初始化方面没有引入重大变更,但在相关领域有一些改进:

auto与返回值推导

C++14允许使用auto作为函数返回类型,这与列表初始化结合提供了更简洁的语法:

#include <iostream>
#include <vector>
#include <utility>// C++14: 使用auto返回值
auto createVector() {return std::vector<int>{1, 2, 3, 4, 5};
}auto createPair() {return std::make_pair("Answer", 42);
}// 返回初始化列表需要指定类型
std::vector<int> getNumbers() {return {1, 2, 3, 4, 5};
}int main() {auto vec = createVector();auto pair = createPair();std::cout << "Vector: ";for (int num : vec) {std::cout << num << " ";}std::cout << std::endl;std::cout << "Pair: " << pair.first << ", " << pair.second << std::endl;return 0;
}

通用lambda表达式

C++14引入的通用lambda表达式可以与列表初始化一起使用,提供更灵活的内联函数:

#include <iostream>
#include <vector>
#include <algorithm>int main() {// 使用初始化列表创建向量std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 使用通用lambda和列表初始化auto processValues = [](auto container, auto processor) {for (auto& item : container) {processor(item);}};// 对奇数加倍,偶数设为零auto transformer = [](int& num) {if (num % 2 == 1) {num *= 2;} else {num = 0;}};// 使用lambda处理向量processValues(numbers, transformer);// 使用列表初始化直接传递数据processValues({100, 200, 300}, [](int& num) {std::cout << "Processing: " << num << std::endl;});// 输出结果std::cout << "Transformed numbers: ";for (int num : numbers) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

列表初始化的最佳实践与常见陷阱

最佳实践

  1. 尽可能使用花括号初始化:花括号初始化提供了更一致、更安全的初始化语法

    int a{42};          // 推荐
    double b{3.14};     // 推荐
    std::vector<int> v{1, 2, 3};  // 推荐
    
  2. 利用防止窄化的特性增强代码安全性

    // 利用防止窄化转换发现潜在问题
    float f = 3.14f;
    // int i{f};  // 编译错误:可能丢失数据
    int i{static_cast<int>(f)};  // 显式转换表明意图
    
  3. 对容器优先使用列表初始化

    // 推荐
    std::vector<int> vec{1, 2, 3, 4, 5};
    std::map<std::string, int> map{{"a", 1}, {"b", 2}};
    
  4. 对类成员使用列表初始化

    class Example {
    private:int value{0};             // 良好的默认初始化std::vector<int> data{};  // 明确初始化为空容器
    };
    

常见陷阱

  1. vector初始化歧义

    std::vector<int> v1(3, 5);  // 3个值为5的元素:[5, 5, 5]
    std::vector<int> v2{3, 5};  // 2个元素,值为3和5:[3, 5]
    
  2. auto与列表初始化的交互

    auto a = {1, 2, 3};  // std::initializer_list<int>,不是vector或数组// 更清晰的方式
    std::vector<int> v{1, 2, 3};  // 明确指定类型
    
  3. 单元素列表vs括号初始化

    std::vector<int> v1(10);    // 10个值为0的元素
    std::vector<int> v2{10};    // 1个值为10的元素
    
  4. 构造函数重载与initializer_list

    class Widget {
    public:Widget(int a, int b) { /* ... */ }Widget(std::initializer_list<int> list) { /* ... */ }
    };Widget w1(10, 20);  // 调用第一个构造函数
    Widget w2{10, 20};  // 调用第二个构造函数(initializer_list),而非第一个
    

    当类既有常规构造函数又有initializer_list构造函数时,花括号初始化优先选择initializer_list构造函数。

  5. 空列表初始化

    std::vector<int> v{};  // 空向量
    int x{};               // 值初始化为0// 对于某些类型,{}和()行为不同
    int a{};   // 值初始化为0
    int b();   // 声明一个名为b的函数,而非变量初始化!
    

实际应用示例

示例1:简化配置对象创建

#include <iostream>
#include <string>
#include <vector>// 应用程序配置
struct AppConfig {std::string appName;std::string version;int maxConnections;bool debugMode;std::vector<std::string> supportedFileTypes;
};// 创建默认配置
AppConfig createDefaultConfig() {return {"MyApp","1.0.0",10,false,{".txt", ".csv", ".json"}};
}// 打印配置
void printConfig(const AppConfig& config) {std::cout << "Application Configuration:" << std::endl;std::cout << "Name: " << config.appName << std::endl;std::cout << "Version: " << config.version << std::endl;std::cout << "Max Connections: " << config.maxConnections << std::endl;std::cout << "Debug Mode: " << (config.debugMode ? "Enabled" : "Disabled") << std::endl;std::cout << "Supported File Types: ";for (const auto& type : config.supportedFileTypes) {std::cout << type << " ";}std::cout << std::endl;
}int main() {// 使用返回的默认配置AppConfig config = createDefaultConfig();printConfig(config);// 直接使用列表初始化创建自定义配置AppConfig customConfig{"CustomApp","2.1.0",20,true,{".xml", ".bin", ".dat"}};std::cout << "\nCustom Configuration:" << std::endl;printConfig(customConfig);return 0;
}

示例2:数据处理管线

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <functional>// 数据处理步骤函数类型
using DataProcessor = std::function<std::vector<double>(const std::vector<double>&)>;// 创建一个处理管线
std::vector<double> processPipeline(const std::vector<double>& input,const std::vector<DataProcessor>& processors
) {std::vector<double> result = input;for (const auto& processor : processors) {result = processor(result);}return result;
}// 数据处理函数
std::vector<double> normalizeData(const std::vector<double>& data) {if (data.empty()) return {};double sum = std::accumulate(data.begin(), data.end(), 0.0);double mean = sum / data.size();std::vector<double> result;result.reserve(data.size());std::transform(data.begin(), data.end(), std::back_inserter(result),[mean](double x) { return x - mean; });return result;
}std::vector<double> squareValues(const std::vector<double>& data) {std::vector<double> result;result.reserve(data.size());std::transform(data.begin(), data.end(), std::back_inserter(result),[](double x) { return x * x; });return result;
}std::vector<double> filterOutliers(const std::vector<double>& data) {if (data.empty()) return {};double threshold = 2.0;std::vector<double> result;std::copy_if(data.begin(), data.end(), std::back_inserter(result),[threshold](double x) { return std::abs(x) <= threshold; });return result;
}int main() {// 使用列表初始化创建输入数据std::vector<double> inputData{1.2, 3.4, 0.5, 7.8, -2.1, 0.0, 5.5};// 使用列表初始化创建处理管线std::vector<DataProcessor> pipeline{normalizeData,squareValues,filterOutliers};// 处理数据auto result = processPipeline(inputData, pipeline);// 打印结果std::cout << "Input data: ";for (double value : inputData) {std::cout << value << " ";}std::cout << std::endl;std::cout << "Processed data: ";for (double value : result) {std::cout << value << " ";}std::cout << std::endl;return 0;
}

示例3:自定义JSON构建器

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <initializer_list>// 简单的JSON值类
class JsonValue {
public:enum class Type { Null, Boolean, Number, String, Array, Object };private:Type type;bool boolValue;double numberValue;std::string stringValue;std::vector<JsonValue> arrayValues;std::map<std::string, JsonValue> objectValues;public:// 构造函数JsonValue() : type(Type::Null) {}JsonValue(bool value) : type(Type::Boolean), boolValue(value) {}JsonValue(int value) : type(Type::Number), numberValue(value) {}JsonValue(double value) : type(Type::Number), numberValue(value) {}JsonValue(const char* value) : type(Type::String), stringValue(value) {}JsonValue(const std::string& value) : type(Type::String), stringValue(value) {}// 数组构造函数JsonValue(std::initializer_list<JsonValue> values) : type(Type::Array), arrayValues(values) {}// 对象构造函数JsonValue(std::initializer_list<std::pair<const std::string, JsonValue>> values): type(Type::Object), objectValues(values) {}// 访问方法Type getType() const { return type; }// 转换为字符串std::string toString() const {std::ostringstream oss;switch (type) {case Type::Null:oss << "null";break;case Type::Boolean:oss << (boolValue ? "true" : "false");break;case Type::Number:oss << numberValue;break;case Type::String:oss << "\"" << stringValue << "\"";break;case Type::Array:oss << "[";for (size_t i = 0; i < arrayValues.size(); ++i) {if (i > 0) oss << ", ";oss << arrayValues[i].toString();}oss << "]";break;case Type::Object:oss << "{";{size_t i = 0;for (const auto& [key, value] : objectValues) {if (i++ > 0) oss << ", ";oss << "\"" << key << "\": " << value.toString();}}oss << "}";break;}return oss.str();}
};int main() {// 使用列表初始化创建JSON值// 简单值JsonValue nullValue;JsonValue boolValue{true};JsonValue numberValue{42};JsonValue stringValue{"Hello, world!"};// 数组JsonValue arrayValue{1, 2, 3, 4, 5};// 嵌套数组JsonValue nestedArray{"fruits",{"apple", "banana", "cherry"}};// 对象JsonValue objectValue{{"name", "John Doe"},{"age", 30},{"isEmployee", true}};// 复杂嵌套对象JsonValue person{{"name", "Alice"},{"age", 28},{"address", {{"street", "123 Main St"},{"city", "Anytown"},{"zipcode", "12345"}}},{"hobbies", {"reading", "hiking", "coding"}}};// 打印结果std::cout << "Null: " << nullValue.toString() << std::endl;std::cout << "Boolean: " << boolValue.toString() << std::endl;std::cout << "Number: " << numberValue.toString() << std::endl;std::cout << "String: " << stringValue.toString() << std::endl;std::cout << "Array: " << arrayValue.toString() << std::endl;std::cout << "Nested Array: " << nestedArray.toString() << std::endl;std::cout << "Object: " << objectValue.toString() << std::endl;std::cout << "Person: " << person.toString() << std::endl;return 0;
}

总结

列表初始化是C++11引入的一个强大特性,它解决了C++初始化语法不一致的问题,提供了统一、安全且灵活的初始化方式。通过花括号{}std::initializer_list,我们可以以一致的方式初始化几乎所有C++类型,从基本类型到复杂容器和用户自定义类型。

列表初始化的主要优势包括:

  1. 语法统一性:提供了一致的初始化语法,适用于几乎所有C++类型
  2. 增强的类型安全:通过防止窄化转换,减少了潜在的数据丢失错误
  3. 简洁性和表达力:使代码更加简洁明了,特别是对于复杂类型和容器
  4. 灵活性:能够轻松处理嵌套初始化和复杂对象构造

在实际应用中,列表初始化已经成为现代C++编程的标准实践,它与其他C++11/14特性(如autodecltype、lambda表达式等)结合使用,可以显著提高代码的可读性和安全性。

在下一篇文章中,我们将继续探索C++11/14中的其他语言特性,包括nullptrconstexprstatic_assert和范围for循环等,这些特性共同构成了现代C++编程的基础。


这是我C++学习之旅系列的第四十三篇技术文章。查看完整系列目录了解更多内容。

相关文章:

C++学习:六个月从基础到就业——C++11/14:列表初始化

C学习&#xff1a;六个月从基础到就业——C11/14&#xff1a;列表初始化 本文是我C学习之旅系列的第四十三篇技术文章&#xff0c;也是第三阶段"现代C特性"的第五篇&#xff0c;主要介绍C11/14中的列表初始化特性。查看完整系列目录了解更多内容。 引言 在C11之前&a…...

城市静音革命:当垃圾桶遇上缓冲器

缓冲垃圾桶的核心原理是通过机械或液压装置实现垃圾桶盖的缓慢闭合&#xff0c;包含以下技术要点&#xff1a;‌能量吸收机制‌液压式&#xff1a;通过活塞挤压油液产生阻尼力&#xff0c;将动能转化为热能耗散弹簧式&#xff1a;利用弹性变形储存和释放能量&#xff0c;配合摩…...

数据库的规范化设计方法---3种范式

第一范式&#xff08;1NF&#xff09;&#xff1a;确保表中的每个字段都是不可分割的基本数据项。 第二范式&#xff08;2NF&#xff09;&#xff1a;在满足1NF的基础上&#xff0c;确保非主属性完全依赖于主键。 第三范式&#xff08;3NF&#xff09;&#xff1a;在满足2NF的基…...

p024基于Django的网上购物系统的设计与实现

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 商品类型管理 商品信息管理 系统管理 订单管理…...

C++跨平台开发:挑战与应对策略

C跨平台开发&#xff1a;挑战与应对策略 在如今设备多样、操作系统碎片化的开发环境中&#xff0c;跨平台能力已成为衡量软件生命力与团队工程效率的重要指标。C 作为高性能系统级语言&#xff0c;在游戏引擎、嵌入式系统、实时渲染等领域依旧坚挺。然而&#xff0c;实现“一次…...

Kotlin 作用域函数(let、run、with、apply、also)对比

Kotlin 的 作用域函数&#xff08;Scope Functions&#xff09; 是简化代码逻辑的重要工具&#xff0c;它们通过临时作用域为对象提供更简洁的操作方式。以下是 let、run、with、apply、also 的对比分析&#xff1a; 一、核心区别对比表 函数上下文对象引用返回值是否扩展函数…...

JavaScript性能优化实战(11):前沿技术在性能优化中的应用

引言 随着Web应用复杂度和性能需求不断提高,传统的JavaScript优化技术已经无法满足某些高性能计算场景的需求。本文将深入探讨前沿Web技术如何突破JavaScript的性能瓶颈,为Web应用提供接近原生应用的性能体验。从底层计算到图形渲染,从并发处理到动画优化,我们将通过实际案…...

数据结构【AVL树】

AVL树 1.AVL树1.AVL的概念2.平衡因子 2.AVl树的实现2.1AVL树的结构2.2AVL树的插入2.3 旋转2.3.1 旋转的原则 1.AVL树 1.AVL的概念 AVL树可以是一个空树。 它的左右子树都是AVL树&#xff0c;且左右子树的高度差的绝对值不超过1。AVL树是一颗高度平衡搜索二叉树&#xff0c;通…...

电动调节V型球阀:行业应用与材质选择全解析

电动调节V型球阀&#xff1a;行业应用与材质选择全解析 作为工业自动化控制中的关键设备&#xff0c;电动调节V型球阀凭借其独特的结构设计与高性能调节能力&#xff0c;在石油、化工、造纸等高要求领域广泛应用。本文将从核心功能、行业应用场景、材质选择要点等方面深入解析…...

页面上如何显示特殊字符、Unicode字符?

在前端开发中&#xff0c;显示特殊字符通常涉及到HTML实体&#xff08;HTML Entities&#xff09;或 Unicode 字符的使用。以下是一些常见的方法来处理特殊字符的显示&#xff1a; 1、HTML实体&#xff1a; HTML为一些常见的特殊字符提供了预定义的实体。例如&#xff0c;要显…...

桌面端进程通信

以下是关于 Electron 桌面端进程通信的基本知识点总结: 一、Electron 进程模型基础 1. 进程类型与职责 进程类型职责权限主进程(Main)创建窗口、系统级操作、IPC中枢完全Node.js访问权限渲染进程(Renderer)展示Web内容、UI交互默认受限(可配置开启Node.js)预加载脚本(Prelo…...

vue2 切换主题色以及单页面好使方法

今天要新增一个页面要根据不同公司切换不同页面主题色&#xff0c;一点一点来&#xff0c;怎么快速更改 el-pagination 分页组件主题色。 <el-pagination :page-size"pageSize" :pager-count"pageCount"layout"sizes, prev, pager, next, jumper,…...

三层固定实体架构:高效实现图上的检索增强生成(RAG)

知识图谱正在成为跨各个领域组织和检索信息的强大工具。它们越来越多地与机器学习和自然语言处理技术相结合,以增强信息检索和推理能力。在本文中,我介绍了一种用于构建知识图谱的三层架构,结合了固定本体实体、文档片段和提取的命名实体。通过利用嵌入和余弦相似度,这种方…...

pnpm 与 npm 的核心区别

以下是 pnpm 与 npm 的核心区别总结&#xff0c;涵盖依赖管理、性能、安全性等关键维度&#xff1a; 1. 依赖存储机制 • npm&#xff1a; 每个项目的依赖独立存储于 node_modules&#xff0c;即使多个项目使用相同版本的包&#xff0c;也会重复下载和存储。例如&#xff0c;1…...

NVMe简介6之PCIe事务层

PCIe的事务层连接了PCIe设备核心与PCIe链路&#xff0c;这里主要基于PCIe事务层进行分析。事务层采用TLP传输事务&#xff0c;完整的TLP由TLPPrefix、TLP头、Payload和TLP Digest组成。TLP头是TLP中最关键的部分&#xff0c;一般由三个或四个双字的长度&#xff0c;其格式定义如…...

【C++详解】string各种接口如何使用保姆级攻略

文章目录 一、string介绍二、string使用构造函数析构函数赋值运算符重载string的遍历修改方法1、下标[]2、迭代器3、范围for 迭代器使用详解const迭代器反向迭代器&#xff08;reverse) Capacity(容量相关)size/lengthmax_sizecapacityclear/emptyshrink_to_fit(缩容)reserve(扩…...

深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器

requestIdleCallback 核心作用 requestIdleCallback 是浏览器提供的 API&#xff0c;用于将非关键任务延迟到浏览器空闲时段执行&#xff0c;避免阻塞用户交互、动画等关键任务&#xff0c;从而提升页面性能体验。 基本语法 const handle window.requestIdleCallback(callb…...

QML鼠标事件和按键事件

1 鼠标事件 1.1 MouseArea组件 在QML中&#xff0c;鼠标事件主要通过MouseArea元素处理&#xff0c;它是用于检测和响应鼠标交互的核心组件。常用属性 cursorShape: 光标形状acceptedButtons: 设置响应鼠标的哪些按键事件&#xff0c;默认为鼠标左键 Qt.LeftButton&#xff1…...

Animaster:一次由 CodeBuddy 主导的 CSS 动画编辑器诞生记

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 起心动念&#xff1a;我想要一个动画编辑器 那天我突然想到&#xff0c;如果能有一个简单好用的 CSS 动画编辑…...

Git 版本控制系统入门指南

Git 版本控制系统入门指南 一、Git 基础概念 1. 什么是 Git&#xff1f; Git 是一个分布式版本控制系统&#xff0c;它可以&#xff1a; 跟踪文件变化协调多人协作管理代码版本支持离线工作保证数据完整性 2. Git 的特点 分布式架构快速分支操作完整历史记录数据完整性保…...

GitHub 趋势日报 (2025年05月16日)

本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1TapXWorld/ChinaTextbookPDF教材。⭐ 4792⭐ 19814Roff2xming521/WeClone&…...

C/C++之内存管理

1. 内存分布 我们定义的变量对于电脑来说也叫数据&#xff0c;同时电脑也会把这些数据分为不同的类型&#xff0c;分别是局部数据&#xff0c;静态数据&#xff0c;全局数据&#xff0c;常量数据和动态申请数据。 在 C 中&#xff0c;各类数据存储位置如下&#xff1a; • 局…...

GitHub文档加载器设计与实现

文章结构&#xff1a; 目录 GitHub文档加载器设计与实现 引言 架构设计 主要组件 核心功能 文档加载流程 加载单个文件 加载目录内容 错误处理与健壮性 分支回退策略 文件类型和大小限制 安全性考虑 SSL证书验证 使用示例 基本使用 测试环境配置 最佳实践 结…...

历史数据分析——中证白酒

简介 中证白酒指数选取涉及白酒生产业务相关上市公司证券作为指数样本,为投资者提供更多样化的投资标的。 估值 中证白酒总体的PB是5.26,在过去十年间位于23.76%,属于较低的水平。 中证白酒总体的PE是20.13,在过去十年间,位于14.24%,属于较低的水平。 从估值的角度似…...

PHP8.0版本导出excel失败

环境&#xff1a;fastadmin框架&#xff0c;不是原版接手的项目。PHP8.0,mysql5.7. code // 创建一个新的 Spreadsheet 对象 $spreadsheet new Spreadsheet(); $worksheet $spreadsheet->getActiveSheet();// 设置表头 $worksheet->setCellValue(A1, ID); $worksheet…...

Seata源码—5.全局事务的创建与返回处理二

大纲 1.Seata开启分布式事务的流程总结 2.Seata生成全局事务ID的雪花算法源码 3.生成xid以及对全局事务会话进行持久化的源码 4.全局事务会话数据持久化的实现源码 5.Seata Server创建全局事务与返回xid的源码 6.Client获取Server的响应与处理的源码 7.Seata与Dubbo整合…...

mac-M系列芯片安装软件报错:***已损坏,无法打开。推出磁盘问题

因为你安装的软件在Intel 或arm芯片的mac上没有签名导致。 首先打开任何来源操作 在系统设置中配置&#xff0c;如下图&#xff1a; 2. 然后打开终端&#xff0c;输入&#xff1a; sudo spctl --master-disable然后输入电脑锁屏密码 打开了任何来源&#xff0c;还遇到已损坏…...

端到端自动驾驶系统实战指南:从Comma.ai架构到PyTorch部署

引言&#xff1a;端到端自动驾驶的技术革命 在自动驾驶技术演进历程中&#xff0c;端到端&#xff08;End-to-End&#xff09;架构正引领新一轮技术革命。不同于传统分模块处理感知、规划、控制的方案&#xff0c;端到端系统通过深度神经网络直接建立传感器原始数据到车辆控制…...

MoveIt Setup Assistant 在导入urdf文件的时候报错

在使用MoveIt Setup Assistant导入urdf文件的时候(load a urdf or collada robot model)&#xff0c;找到urdf文件后MoveIt Setup Assistant闪退并报错&#xff1a; Warning: Ignoring XDG_SESSION_TYPEwayland on Gnome. Use QT_QPA_PLATFORMwayland to run on Wayland anyway…...

uniapp +vue +springboot多商家订餐系统

uniapp vue springboot多商家订餐系统&#xff0c;这个系统我整理调试的多商家&#xff0c;多用户的&#xff0c;多端小程序订餐系统&#xff0c;主要包含了uniapp小程序端&#xff0c;管理后台页面vue端&#xff0c;后台功能接口Springboot端&#xff0c;源码齐全&#xff0c;…...

docker迅雷自定义端口号、登录用户名密码

在NAS上部署迅雷&#xff0c;确实会带来很大的方便。但是目前很多教程都是讲怎么部署docker迅雷&#xff0c;鲜有将自定义配置的方法。这里讲一下怎么部署&#xff0c;并重点讲一下支持的自定义参数。 一、部署docker 在其他教程中&#xff0c;都是介绍的如下命令&#xff0c…...

联想笔记本黑屏了,排线出问题还是静电

以下引用 联想电脑屏幕不亮&#xff0c;电源键和键盘灯均正常的解决办法&#xff08;超简单&#xff09;_拯救者屏幕不亮,键盘有电-CSDN博客 昨天正常关机后&#xff0c;今天一早来工位打开电脑&#xff0c;美美开始玩手机。 一会之后抬头屏幕是黑的&#xff0c;还以为自动息…...

uniapp -- uCharts 仪表盘刻度显示 0.9999999 这样的值问题处理。

文章目录 🍉问题🍉解决方案🍉问题 在仪表盘上,23.8变成了 23.799999999999997 🍉解决方案 formatter格式化问题 1:在 config-ucharts.js 或 config-echarts.js 配置对应的 formatter 方法 formatter: {yAxisDemo1: function (...

为 Spring Boot 应用程序构建 CI/CD 流水线

为 Spring Boot 应用程序创建构建/部署流水线涉及多个步骤&#xff0c;而 Jenkins 可以作为强大的工具来自动化这些流程。在本教程中&#xff0c;我们将指导您为托管在 GitHub 上的 Spring Boot 应用程序设置流水线&#xff0c;使用 Jenkins 构建该应用程序&#xff0c;并将其部…...

数值分析填空题速通

填空题速通 文章目录 填空题速通误差与误差传播均差插值与误差范数、赋范线性空间与内积、内积空间范数代数精度数值微分积分误差迭代方程与收敛阶微分方程数值解法的迭代公式与阶 误差与误差传播 例 设 a 1.414 a 1.414 a1.414&#xff0c; b − 0.576 b -0.576 b−0.57…...

day016-系统负载压力测试-磁盘管理

文章目录 1. 系统负载2. 模拟系统高负载2.1 模拟cpu负载2.2 模拟IO负载 3. 磁盘接口分类4. 思维导图 1. 系统负载 系统负载是衡量系统繁忙程度的指标负载值接近或超过cpu核心总数表示系统负载高负载高常见原因&#xff1a;1.占用cpu过多导致2.占用磁盘IO过多导致&#xff08;I…...

DeepSeek指令微调与强化学习对齐:从SFT到RLHF

后训练微调的重要性 预训练使大模型获得丰富的语言和知识表达能力,但其输出往往与用户意图和安全性需求不完全匹配。业内普遍采用三阶段训练流程:预训练 → 监督微调(SFT)→ 人类偏好对齐(RLHF)。预训练阶段模型在大规模语料上学习语言规律;监督微调利用人工标注的数据…...

安全性(一):加密算法总结

一、加密算法分类总览 加密类型关键特性代表算法主要用途对称加密加解密使用同一个密钥DES、3DES、AES、SM4数据加密传输、存储非对称加密公钥加密&#xff0c;私钥解密&#xff08;或反向&#xff09;RSA、DSA、ECC、SM2密钥交换、数字签名、身份认证哈希算法不可逆摘要MD5、…...

DeepSeek 赋能军事:重塑现代战争形态的科技密码

目录 一、引言&#xff1a;AI 浪潮下的军事变革与 DeepSeek 崛起二、DeepSeek 技术原理与特性剖析2.1 核心技术架构2.2 独特优势 三、DeepSeek 在军事侦察中的应用3.1 海量数据快速处理3.2 精准目标识别追踪3.3 预测潜在威胁 四、DeepSeek 在军事指挥决策中的应用4.1 战场态势实…...

我司助力高校打造「智慧创新AI学习中心」

为推动AI教育融合跨领域应用&#xff0c;东吴大学于2025年4月举行「智慧创新AI学习中心」揭牌仪式&#xff0c;并宣布正式启动AI特色课程与教学空间建置计画。此次建置由我司协助整体教室空间与设备规划&#xff0c;导入最新NVIDIA GeForce RTX 50系列桌上型电脑&#xff0c;并…...

AI赋能把“杂多集合”转化为“理想集合”的数学建模与认知升级

AI赋能把“杂多集合”转化为“理想集合”的数学建模与认知升级 一、核心概念定义 杂多集合&#xff08;Chaotic Set&#xff09; 定义&#xff1a;元素间关系模糊、结构无序的集合 数学表达&#xff1a;C{x∣x∈X,P(x)}&#xff0c;其中 P(x) 是模糊隶属函数 实例&#xf…...

NVC++ 介绍与使用指南

文章目录 NVC 介绍与使用指南NVC 简介安装 NVC基本使用编译纯 C 程序编译 CUDA C 程序 关键编译选项示例代码使用标准并行算法 (STDPAR)混合 CUDA 和 C 优势与限制优势限制 调试与优化 NVC 介绍与使用指南 NVC 是 NVIDIA 提供的基于 LLVM 的 C 编译器&#xff0c;专为 GPU 加速…...

Redis 事务与管道:原理、区别与应用实践

在现代分布式系统开发中&#xff0c;Redis 作为高性能的内存数据库&#xff0c;其事务处理和管道技术是开发者必须掌握的核心知识点。本文将深入探讨 Redis 事务和管道的实现原理、使用场景、性能差异以及最佳实践&#xff0c;帮助开发者根据实际需求选择合适的技术方案。 一、…...

Git 多人协作

目录 情景一 情景二 合并分支 情景一 目标&#xff1a;远程 master 分支下的 file.txt 文件新增代码 "aaa","bbb"。 实现&#xff1a;由开发者1新增 "aaa" ,开发者2新增 bbb。 条件&#xff1a;在一个分支下合作完成。 针对以上情景我们要注意…...

Unity 人物模型学习笔记

一、关于模型的检查 拿到人物模型时&#xff0c;检查人物&#xff1a; 位置信息是否在0点布线/UV是否正常身体各部分是否分开各部分命名是否清晰骨骼需要绑定 二、Unity人物动画 https://www.bilibili.com/video/BV1cc41197mF?spm_id_from333.788.recommend_more_video.-1&a…...

【和春笋一起学C++】(十四)指针与const

将const用于指针&#xff0c;有两种情况&#xff1a; const int *pt; int * const pt; 目录 1. const int *pt 2. int * const pt 3. 扩展 1. const int *pt 首先看第一种情况&#xff0c;const在int的前面&#xff0c;有如下语句&#xff1a; int peoples12&#xff1…...

AI知识梳理——RAG、Agent、ReAct、LangChain、LangGraph、MCP、Function Calling、JSON-RPC

AI技术I AI技术II RAG &#x1f4cc; 高度凝练表达 RAG &#xff08;检索增强生成&#xff09;是一种结合信息检索与生成式人工智能的技术框架&#xff0c;旨在提升大型语言模型&#xff08;LLM&#xff09;的输出准确性和实用性。通过在生成响应前引入外部知识库的信息&#…...

ModuleNotFoundError: No module named ‘SDToolbox‘

(py311) C:>python Python 3.11.11 | packaged by Anaconda, Inc. | (main, Dec 11 2024, 16:34:19) [MSC v.1929 64 bit (AMD64)] on win32 Type “help”, “copyright”, “credits” or “license” for more information. from SDToolbox import PostShock_eq Tracebac…...

在宝塔中使用.NET环境管理部署 .NET Core项目

本次采用的演示环境未腾讯云轻量级服务器&#xff0c;使用应用模板&#xff0c;选择宝塔liunx面板。 一、登录宝塔安装环境 直接选择免密登录 进入腾讯云webshell后&#xff0c;输入bt&#xff0c;选择14 选择网站选择.NET项目安装.NET环境管理安装对应的.NET 版本 注意&…...

【DAY21】 常见的降维算法

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 目录 PCA主成分分析 t-sne降维 线性判别分析 (Linear Discriminant Analysis, LDA) 作业&#xff1a; 什么时候用到降维 降维的主要应用场景 知识点回顾&#xff1a; PCA主成分分析t-sne降维LDA线性判别 通常情况下&#xff0c;…...