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

news2025/5/18 5:39:34

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;  // 输出 1
    std::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;  // 输出 2
    std::cout << "vec4 size: " << vec4.size() << std::endl;  // 输出 1
    std::cout << "vec5 size: " << vec5.size() << std::endl;  // 输出 5
    
    return 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推导为int
    printValues({"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};                                  // 默认初始化为0
    std::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();
    
    // 使用成员初始化器的Widget
    std::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++学习之旅系列的第四十三篇技术文章。查看完整系列目录了解更多内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文阅读】人脸修复(face restoration ) 不同先验代表算法整理2

文章目录 一、前述二、不同的先验及代表性论文2.1 几何先验&#xff08;Geometric Prior&#xff09;2.2 生成式先验&#xff08;Generative Prior&#xff09;2.3 codebook先验&#xff08;Vector Quantized Codebook Prior&#xff09;2.4 扩散先验 &#xff08;Diffusion Pr…

无监督学习在医疗AI领域的前沿:多模态整合、疾病亚型发现与异常检测

引言 人工智能技术在医疗领域的应用正经历着从辅助决策向深度赋能的转变。无监督学习作为人工智能的核心范式之一,因其无需大量标注数据、能够自动发现数据内在规律的特性,在医疗AI领域展现出独特优势。尤其在2025年,无监督学习技术在医疗AI应用中呈现出多模态整合、疾病亚…

计算机操作系统概要

不谋万世者&#xff0c;不⾜谋⼀时。不谋全局者 &#xff0c;足谋⼀域 。 ——陈澹然《寤⾔》《迁都建藩议》 操作系统 一.对文件简单操作的常用基础指令 ls ls 选项 目录或⽂件名:罗列当前⽬录下的⽂件 -l&#xff1a;以长格式显示⽂件和⽬录的详细信息 -a 或 --all&…

图片通过滑块小图切换大图放大镜效果显示(Vue3)

图片通过滑块小图切换大图放大镜效果显示 实现目标&#xff1a; 显示一组图片列表&#xff0c;鼠标进入小图记录当下小图下标&#xff0c;通过小图下标在数组中对应图片显示到大图位置&#xff1b; 鼠标进入大图位置时&#xff0c;带动滑块移动&#xff0c;并将放大两倍的大图…

[SSL]1Panel添加阿里云DNS账户

1 创建一个子用户 将得到的key和secret贴到1panel的DNS账户配置中 添加权限 即可用DNS账号申请SSL证书

计算机网络 : 网络基础

计算机网络 &#xff1a; 网络基础 目录 计算机网络 &#xff1a; 网络基础引言1. 网络发展背景2. 初始协议2.1 初始协议2.2 协议分层2.2.1 软件分层的好处2.2.2 OSI七层模型2.2.3 TCP/IP五层&#xff08;四层&#xff09;模型 2.3 TCP/IP协议2.3.1TCP/IP协议与操作系统的关系&…

C++跨平台开发:突破不同平台的技术密码

Windows 平台开发经验 开发环境搭建 在 Windows 平台进行 C 开发&#xff0c;最常用的集成开发环境&#xff08;IDE&#xff09;是 Visual Studio。你可以从Visual Studio 官网下载安装包&#xff0c;根据安装向导进行安装。安装时&#xff0c;在 “工作负载” 界面中&#xff…

第一次做逆向

题目来源&#xff1a;ctf.show 1、下载附件&#xff0c;发现一个exe和一个txt文件 看看病毒加没加壳&#xff0c;发现没加那就直接放IDA 放到IDA找到main主函数&#xff0c;按F5反编译工具就把他还原成类似C语言的代码 然后我们看逻辑&#xff0c;将flag.txt文件的内容进行加…

【Linux网络】传输层协议TCP

TCP协议 TCP全称为"传输控制协议(TransmissionControl Protocol"). 人如其名, 要对数据的传输进行一个详细的控制; TCP协议段格式 源、目的端口号&#xff1a;表示数据从哪个进程来&#xff0c;到哪个进程去。 32位序号、确认序号 4位TCP报头长度:表示该TCP头部有…

AAAI-2025 | 中科院无人机导航新突破!FELA:基于细粒度对齐的无人机视觉对话导航

作者&#xff1a;Yifei Su, Dong An, Kehan Chen, Weichen Yu, Baiyang Ning, Yonggen Ling, Yan Huang, Liang Wang 单位&#xff1a;中国科学院大学人工智能学院&#xff0c;中科院自动化研究所模式识别与智能系统实验室&#xff0c;穆罕默德本扎耶德人工智能大学&#xff0…

排序算法之基础排序:冒泡,选择,插入排序详解

排序算法之基础排序&#xff1a;冒泡、选择、插入排序详解 前言一、冒泡排序&#xff08;Bubble Sort&#xff09;1.1 算法原理1.2 代码实现&#xff08;Python&#xff09;1.3 性能分析 二、选择排序&#xff08;Selection Sort&#xff09;2.1 算法原理2.2 代码实现&#xff…

Linux常用命令42——tar压缩和解压缩文件

在使用Linux或macOS日常开发中&#xff0c;熟悉一些基本的命令有助于提高工作效率&#xff0c;tar 是 Linux 和 Unix 系统中用于归档文件和目录的强大命令行工具。tar 名字来自 "tape archive"&#xff08;磁带归档&#xff09;&#xff0c;最初用于将文件打包到磁带…

网络协议分析 实验七 FTP、HTTP、DHCP

文章目录 实验7.1 FTP协议练习二 使用浏览器登入FTP练习三 在窗口模式下&#xff0c;上传/下传数据文件实验7.2 HTTP(Hyper Text Transfer Protocol)练习二 页面提交练习三 访问比较复杂的主页实验7.3 DHCP(Dynamic Host Configuration Protocol) 实验7.1 FTP协议 dir LIST&…

HTML 表格与div深度解析区别及常见误区

一、HTML<div>元素详解 <div>是HTML中最基本的块级容器元素&#xff0c;本身没有语义&#xff0c;主要用于组织和布局页面内容。以下是其核心用法&#xff1a; 1. 基础结构与特性 <div><!-内部可包含任意HTML元素 --><h2>标题</h2><p…

linux-进程信号的产生

Linux中的进程信号&#xff08;signal&#xff09;是一种用于进程间通信或向进程传递异步事件通知的机制。信号是一种软中断&#xff0c;用于通知进程某个事件的发生&#xff0c;如错误、终止请求、计时器到期等。 1. 信号的基本概念 - 信号&#xff08;Signal&#xff09;&am…

内容中台重构企业知识管理路径

智能元数据驱动知识治理 现代企业知识管理的核心挑战在于海量非结构化数据的有效治理。通过智能元数据分类引擎&#xff0c;系统可自动识别文档属性并生成多维标签体系&#xff0c;例如将技术手册按产品版本、功能模块、适用场景进行动态标注。这种动态元数据框架不仅支持跨部…

基于Spring Boot+Layui构建企业级电子招投标系统实战指南

一、引言&#xff1a;重塑招投标管理新范式 在数字经济浪潮下&#xff0c;传统招投标模式面临效率低、透明度不足、流程冗长等痛点。本文将以Spring Boot技术生态为核心&#xff0c;融合Mybatis持久层框架、Redis高性能缓存及Layui前端解决方案&#xff0c;构建一个覆盖招标代理…

Kali安装详细图文安装教程(文章内附有镜像文件连接提供下载)

Kali镜像文件百度网盘&#xff1a;通过网盘分享的文件&#xff1a;kali-linux-2024.2-installer-amd64.iso 链接: https://pan.baidu.com/s/1MfCXi9KrFDqfyYPqK5nbKQ?pwdSTOP 提取码: STOP --来自百度网盘超级会员v5的分享 1.下载好镜像文件后&#xff0c;我们打开我们的VMwa…

2.4GHz无线芯片核心技术解析与典型应用

2.4G芯片作为工作在2.4GHz ISM频段的无线通信集成电路&#xff0c;主要面向短距离数据传输应用。这类芯片具有以下技术特点&#xff1a; 多协议支持 兼容蓝牙、Wi-Fi和ZigBee等主流协议 采用SDR技术实现协议灵活切换 适用于智能家居和物联网设备 低功耗特性 采用休眠唤醒和动态…

Chrome代理IP配置教程常见方式附问题解答

在网络隐私保护和跨境业务场景中&#xff0c;为浏览器配置代理IP已成为刚需。无论是访问地域限制内容、保障数据安全&#xff0c;还是管理多账号业务&#xff0c;掌握Chrome代理配置技巧都至关重要。本文详解三种主流代理设置方式&#xff0c;助你快速实现精准流量管控。 方式一…