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: 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: 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: 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: 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: 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已讲过的拥有接口。
先备完成后:阅读这一阶段的源码
// 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: 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仍由学习者完成;教材执行结果不写入个人学习验收。