← 完整学习路线

ONE CONTINUOUS CASE / C++20

一批测量数据:按先备选择阶段

这组教学示例围绕读数[3,1,4,1,5]展开:5项、总和14,平均值问题的期望为2.8。它提供同一业务的不同应用,概念由对应章节讲授。先完成章节中的预测、单条件变体与独立练习,再回访这里;运行教材例子不等于个人学习验收。

当前入口与完整源码的阅读边界

刚开始学习时进入基础01:第一份程序。阶段编号是文件标识,不等于章号。完整阅读顺序是:第09章后的01→02→03;第15章指定补授后的06;第16章后的05;第17章的stage-07-migrated;并行先备完成后的08。

文件/阶段 当前阅读入口 这次应用与给定结果 边界
stage-01.cpp 09.2范围遍历之后 count=5、total=14;空组0、单项5 包含array与范围for,不回填新01
stage-02.cpp 新09之后,回用04函数05引用 原始总和14、校准总和24;原值3、返回5 新04/05先完成各自标量练习
stage-03.cpp 08拥有序列09借用 owner=14、copy=24、borrowed=14 完整源码按vector/span实际合同读,不把类型当黑盒
stage-06.cpp 15.4的小整数输入与显式T→span组合完成后 新拥有者14、快照24、阈值计数3;字节输入和260 还需已完成10的算法/lambda、12–14的拥有与移动;该补授未完成时不整读
stage-05.cpp 16.4–16.5异常与清理 batch=14、saved_after_scope=3、rejected=2 不把throw/catch提前移回标量类入门
stage-07-migrated.cpp 17.4工程回访 batch_cases=5、parsing_checks=5、split_total=14 使用当前工程整合版本;阅读前完成解析与构建先备
stage-08.cpp 第25章线程句柄末尾构造与捕获第31章分区 parallel_total=14;三段4+5+5=14 仍须掌握实际守卫与弃值占位读法;扩展到队列/原子时,先完成第26/27章对应合同

下载当前stage-07-migrated.cpp。它沿用16章已授的“先检查有值,再取值”,保持原五组批次和五个解析输入;文件名明确标识当前工程整合版本。

局部READ只在对应片段先备成立时使用,不由文件可展开就推定整份已读。算法/lambda的当前练习入口是新10;不再依靠“先看borrowed这几行、以后再懂它从哪里来”的学习顺序。

输入合同与本人任务

本套程序约定每批最多1024个读数、每项在[-1000000,1000000]。使用long long的阶段,其给定有界总和绝对值最多1024000000。阶段01–03仅使用源码中更小的固定输入;阶段02的偏移仅使用已列的-1、0、2等值。数值类型与范围检查回用08.5

阶段06依赖调用者提供符合范围的输入;阶段05构造和阶段08入口会检查相应数量/值域。阶段05的1024个最大合法读数合计1024000000,并拒绝1025项。阶段06仅对列出的int、short与unsigned char小输入作教学对照;范围边界沿用15.4,不把integral约束当数值安全证明。

完成一个已准入阶段后,先记录源码身份、原输入和期望,再在自己的副本只改一个条件。例如stage01把第二项1改2时,先写新期望15,再安排对应检查;测试表增加输入时也要更新独立期望,长度与漏注册核对见18.1–18.2。在自己的副本完成修改,并保留原始输入和预测。

运行当前选定阶段

命令与“一份main一个程序”的读法见编译指南基础17。完成新09先备后,在下载了stage01的目录可运行:

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror stage-01.cpp -o stage-01
./stage-01

该文件给定输出为:

count=5 total=14
empty=0 single=5

stage-07-migrated.cpp单独下载、单独构建:

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -g -O0 stage-07-migrated.cpp -o stage-07-migrated
./stage-07-migrated

预期为batch_cases=5 parsing_checks=5 split_total=14,退出0。检查/退出状态的用途回用18.2;源码版本不同,记录也分别保存。

stage08的实际任务和证据边界

在第31章分区教学后,先验算本题给定区间[0,2)、[2,4)、[4,5),对应局部和4、5、5、合计14,再回访线程源。输入、partial、线程句柄的先备与寿命合同引用第25章的两段读法,不在项目说明中再开一套线程课。

原stage08的main没有外层匹配catch,不能凭进程退出或源码里出现JoinAll,就声称启动异常后的析构已经执行;16.4的未捕获异常边界后续NET N3观察说明该区分。队列关闭和原子发布是第26章第27章各自的扩展,stage08没有实现它们。

这些教学示例不替代学习者的串行或并行实现与独立验收。

各阶段的学习顺序与先备见上方说明。阶段07使用下方单独提供的当前版本;只阅读和运行当前任务要求的文件。

STAGE 01

stage-01.cpp

先完成上方准入表中的章节。单元 17 · view、失效与中段独立验收M1 →

先备完成后:阅读这一阶段的源码
stage-01.cpp
// Stage 01: read one value, update one accumulator. Original teaching code.
#include <array>
#include <iostream>
int main() {
    // Five existing integers. Positions are 0, 1, 2, 3, 4.
    const std::array<int,5> readings{3,1,4,1,5};
    int total=0;
    for(const int reading:readings) {
        total+=reading; // After each pass: 3, 4, 8, 9, 14.
    }
    if(total!=14 || readings.size()!=5) return 1;
    int empty_total=0;
    for(const int reading:std::array<int,0>{}) empty_total+=reading;
    int single_total=0;
    for(const int reading:std::array<int,1>{5}) single_total+=reading;
    if(empty_total!=0 || single_total!=5) return 2;
    std::cout<<"count="<<readings.size()<<" total="<<total<<'\n';
    std::cout<<"empty="<<empty_total<<" single="<<single_total<<'\n';
}
下载当前阶段源码

STAGE 02

stage-02.cpp

先完成上方准入表中的章节。单元 17 · view、失效与中段独立验收M1 →

先备完成后:阅读这一阶段的源码
stage-02.cpp
// Stage 02: functions name a step; references explicitly permit a change.
#include <array>
#include <iostream>
int calibrated(int reading,int offset) {
    return reading+offset; // The caller's reading is not modified.
}
void add_reading(int& total,int reading) {
    total+=reading; // This name refers to the caller's accumulator.
}
int main() {
    const std::array<int,5> readings{3,1,4,1,5};
    int raw=0,adjusted=0;
    for(const int reading:readings) {
        add_reading(raw,reading);
        add_reading(adjusted,calibrated(reading,2));
    }
    int original=3;
    const int copy=calibrated(original,2);
    int zero=0;add_reading(zero,0);
    if(raw!=14 || adjusted!=24 || original!=3 || copy!=5 || zero!=0) return 1;
    std::cout<<"raw="<<raw<<" calibrated="<<adjusted<<'\n';
    std::cout<<"original="<<original<<" returned="<<copy<<'\n';
}
下载当前阶段源码

STAGE 03

stage-03.cpp

先完成上方准入表中的章节。单元 17 · view、失效与中段独立验收M1 →

先备完成后:阅读这一阶段的源码
stage-03.cpp
// Stage 03: vector owns values; span borrows a contiguous range and length.
#include <iostream>
#include <span>
#include <vector>
int total_of(std::span<const int> readings) {
    int total=0;
    for(const int value:readings) total+=value;
    return total; // The small teaching inputs keep the sum within int.
}
int main() {
    const std::vector<int> readings{3,1,4,1,5};
    const std::span<const int> view{readings};
    auto independent=readings; // Own a copy of all five values.
    independent[0]+=10;
    if(total_of(view)!=14 || total_of(independent)!=24) return 1;
    if(total_of(std::span<const int>{})!=0) return 2;
    const std::vector<int> single{5};
    if(total_of(single)!=5) return 3;
    // The owner is still alive; no operation reallocates its storage.
    std::cout<<"owner="<<total_of(readings)<<" copy="<<total_of(independent)
             <<" borrowed="<<total_of(view)<<'\n';
}
下载当前阶段源码

STAGE 06

stage-06.cpp

先完成上方准入表中的章节。单元 29 · concepts与泛型可见性 →

先备完成后:阅读这一阶段的源码
stage-06.cpp
// Stage 06: read in two passes: ownership first, generic summation later.
#include <algorithm>
#include <concepts>
#include <iostream>
#include <memory>
#include <span>
#include <utility>
#include <vector>
struct ReadingBatch {std::vector<int> readings;};
template<std::integral T>
long long total_of(std::span<const T> readings) {
    long long total=0;
    for(const T value:readings) total+=value;
    return total; // Only the documented small teaching values are supplied; this is not checked arithmetic.
}
int main() {
    auto owner=std::make_unique<ReadingBatch>(ReadingBatch{{3,1,4,1,5}});
    const std::span<const int> borrowed{owner->readings};
    auto snapshot=*owner;snapshot.readings[0]+=10;
    auto next_owner=std::move(owner); // Transfer the pointer, not the pointee.
    if(owner || !next_owner) return 1;
    int threshold=3;
    const auto large=[threshold](int reading){return reading>=threshold;};
    const auto count=std::count_if(borrowed.begin(),borrowed.end(),large);
    const std::vector<short> small_type{3,1,4,1,5};
    const std::vector<unsigned char> bytes{250,10};
    if(total_of<int>(borrowed)!=14 || total_of<int>(snapshot.readings)!=24 ||
       total_of<short>(small_type)!=14 || total_of<unsigned char>(bytes)!=260 || count!=3 ||
       total_of<int>(std::span<const int>{})!=0) return 2;
    std::cout<<"owner_empty="<<(!owner)<<" next=14 snapshot=24 large="<<count<<'\n';
    std::cout<<"generic_int=14 generic_short=14\n";
}
下载当前阶段源码

STAGE 05

stage-05.cpp

先完成上方准入表中的章节。单元 31 · 异常、RAII展开与noexcept →

先备完成后:阅读这一阶段的源码
stage-05.cpp
// Stage 05: a class owns its vector and protects a small, explicit value range.
#include <iostream>
#include <span>
#include <stdexcept>
#include <vector>
class ReadingBatch {
    std::vector<int> readings_;
public:
    explicit ReadingBatch(const std::vector<int>& readings):readings_(readings) {
        if(readings_.size()>1024) throw std::invalid_argument("batch too large");
        for(const int value:readings_)
            if(value < -1000000 || value > 1000000)
                throw std::invalid_argument("reading range");
    }
    std::span<const int> view() const {return readings_;}
    long long total() const {
        long long result=0;
        for(const int value:readings_) result+=value;
        return result;
    }
    // Rule of Zero: vector releases its storage when this batch is destroyed.
    // No handwritten delete, copy operation, or destructor is necessary.
};
int main() {
    const ReadingBatch batch{{3,1,4,1,5}};
    long long saved=0;
    {const ReadingBatch temporary{{1,2}};saved=temporary.total();}
    // saved owns a number. It does not borrow the destroyed temporary's storage.
    int rejected=0;
    try {const ReadingBatch bad{{1000001}};} catch(const std::invalid_argument&) {++rejected;}
    try {const ReadingBatch bad{std::vector<int>(1025,0)};} catch(const std::invalid_argument&) {++rejected;}
    const ReadingBatch empty{std::vector<int>{}},boundary{{-1000000,1000000}};
    const ReadingBatch largest{std::vector<int>(1024,1000000)};
    if(batch.total()!=14 || batch.view().size()!=5 || saved!=3 || rejected!=2 ||
       empty.view().size()!=0 || empty.total()!=0 || boundary.total()!=0 ||
       largest.total()!=1024000000LL) return 1;
    std::cout<<"batch=14 saved_after_scope="<<saved<<" rejected="<<rejected<<'\n';
}
下载当前阶段源码

STAGE 07

stage-07-migrated.cpp

先完成上方准入表中的章节。单元 32 · 分离编译、链接与可复现构建 →

当前版本回用17.4已讲过的拥有接口。

先备完成后:阅读这一阶段的源码
stage-07-migrated.cpp
// Textbook stage-07 migration: check optional state before reading its value.
// Stage 07: independent expected values, always-on checks, and strict input parsing.
#include <charconv>
#include <cstddef>
#include <iostream>
#include <optional>
#include <span>
#include <string_view>
#include <vector>
struct Summary {std::size_t count;long long total;};
Summary summarize(std::span<const int> values) {
    Summary result{values.size(),0};
    for(const int value:values) result.total+=value;
    return result;
}
std::optional<int> parse_reading(std::string_view text) {
    if(text.empty()) return std::nullopt;
    int value=0;
    const auto [end,error]=std::from_chars(text.data(),text.data()+text.size(),value);
    if(error!=std::errc{} || end!=text.data()+text.size() || value < -1000000 || value > 1000000)
        return std::nullopt;
    return value;
}
int main() {
    const std::vector<std::vector<int>> cases{{3,1,4,1,5},{},{5},{-3,1},{0,0}};
    const std::vector<long long> expected{14,0,5,-2,0};
    for(std::size_t i=0;i<cases.size();++i) {
        const auto actual=summarize(cases[i]);
        if(actual.total!=expected[i] || actual.count!=cases[i].size()) return 1;
    }
    const auto three = parse_reading("3");
    const auto lower = parse_reading("-1000000");
    if(!three || *three!=3 || !lower || *lower!=-1000000 ||
       parse_reading("3x") || parse_reading("") || parse_reading("1000001")) return 2;
    const std::vector<int> left{3,1},right{4,1,5};
    if(summarize(left).total+summarize(right).total!=14) return 3;
    std::cout<<"batch_cases="<<cases.size()<<" parsing_checks=5 split_total=14\n";
}
下载当前阶段源码

STAGE 08

stage-08.cpp

先完成上方准入表中的章节。单元 50 · 把两个固定任务推广为完整分区 →

先备完成后:阅读这一阶段的源码
stage-08.cpp
// Stage 08: optional preview after chapter 14; concurrency is taught in chapters 21-27.
#include <algorithm>
#include <cstddef>
#include <iostream>
#include <span>
#include <stdexcept>
#include <thread>
#include <utility>
#include <vector>
std::pair<std::size_t,std::size_t> partition(std::size_t n,std::size_t p,std::size_t id) {
    if(p==0 || id>=p) throw std::invalid_argument("partition");
    const auto q=n/p,r=n%p,begin=id*q+std::min(id,r);
    return {begin,begin+q+(id<r?1:0)};
}
long long parallel_total(std::span<const int> input,std::size_t p) {
    if(p==0 || p>8 || input.size()>1024) throw std::invalid_argument("bounded workload");
    for(const int value:input)
        if(value < -1000000 || value > 1000000) throw std::invalid_argument("reading range");
    std::vector<long long> partial(p,0);
    std::vector<std::thread> workers;workers.reserve(p);
    struct JoinAll {
        std::vector<std::thread>& threads;
        ~JoinAll(){for(auto& thread:threads) if(thread.joinable()) thread.join();}
    } join_all{workers};
    for(std::size_t id=0;id<p;++id) {
        const auto [begin,end]=partition(input.size(),p,id);
        workers.emplace_back([&,id,begin,end]{
            long long local=0;
            for(auto i=begin;i<end;++i) local+=input[i];
            partial[id]=local; // Each thread writes its own existing slot once.
        });
    }
    for(auto& thread:workers) thread.join();
    long long total=0;for(const auto value:partial) total+=value;
    return total;
}
int main() {
    const std::vector<int> readings{3,1,4,1,5},single{5};
    for(std::size_t p=1;p<=8;++p) {
        if(parallel_total(readings,p)!=14 || parallel_total({},p)!=0 || parallel_total(single,p)!=5) return 1;
        std::size_t previous=0;
        for(std::size_t id=0;id<p;++id) {
            const auto [begin,end]=partition(readings.size(),p,id);
            if(begin!=previous || end<begin || end>readings.size()) return 2;
            previous=end;
        }
        if(previous!=readings.size()) return 3;
    }
    int rejected=0;
    for(const std::size_t p:{0U,9U})
        try{(void)parallel_total(readings,p);}catch(const std::invalid_argument&){++rejected;}
    if(rejected!=2) return 4;
    std::cout<<"parallel_total="<<parallel_total(readings,3)<<" worker_cases=8 rejected=2\n";
    std::cout<<"ranges=0:2,2:4,4:5\n";
}
下载当前阶段源码

这些是公开教学样例。Reduction Lab的LEARNER_TODO仍由学习者完成;教材执行结果不写入个人学习验收。