diff --git a/.github/workflows/dslings-ref-ci.yml b/.github/workflows/dslings-ref-ci.yml new file mode 100644 index 0000000..1cbfd2d --- /dev/null +++ b/.github/workflows/dslings-ref-ci.yml @@ -0,0 +1,115 @@ +name: Validate dslings reference solutions + +on: + push: + branches: ["main"] + paths: + - "dslings/**" + - "solutions/**" + - "d2x/**" + - "xmake.lua" + - ".github/workflows/dslings-ref-ci.yml" + pull_request: + paths: + - "dslings/**" + - "solutions/**" + - "d2x/**" + - "xmake.lua" + - ".github/workflows/dslings-ref-ci.yml" + workflow_dispatch: + +jobs: + build-and-run-reference: + runs-on: ubuntu-latest + + env: + XLINGS_VERSION: "0.4.3" + + steps: + - uses: actions/checkout@v4 + + - name: Install Xlings ${{ env.XLINGS_VERSION }} + run: | + set -eu + curl -fsSL "https://github.com/d2learn/xlings/releases/download/v${XLINGS_VERSION}/xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" \ + | tar -xzf - -C /tmp + /tmp/xlings-${XLINGS_VERSION}-linux-x86_64/bin/xlings self install + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + + - name: Install xmake (via xlings) + run: xlings install xmake -y + + - name: xmake config + run: xmake f -y + + # Following steps go through the SAME `xmake d2x-buildtools` plugin + # path that `d2x checker` uses internally (see d2x/buildtools/xmake/main.lua). + # This way the CI exercises the real d2x integration code path, not a + # parallel xmake-only path. A passing CI here is what a learner would + # see if they ran `d2x checker cpp11-XX-feat-K-ref` locally. + + - name: List all *-ref targets via d2x-buildtools + id: list_ref + run: | + set -eu + REF_TARGETS=$(xmake d2x-buildtools --command=list 2>/dev/null \ + | tr ' ' '\n' \ + | sed 's/@.*$//' \ + | grep -E '\-ref$' \ + | sort -u) + echo "Found $(echo "$REF_TARGETS" | wc -l) reference targets:" + echo "$REF_TARGETS" + echo "targets<> "$GITHUB_OUTPUT" + echo "$REF_TARGETS" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + - name: Build all *-ref targets via d2x-buildtools + run: | + set -eu + while IFS= read -r tgt; do + [ -z "$tgt" ] && continue + echo "::group::build $tgt" + xmake d2x-buildtools --command=build --target="$tgt" + echo "::endgroup::" + done <<< "${{ steps.list_ref.outputs.targets }}" + + - name: Run all *-ref targets via d2x-buildtools and assert no ❌ + run: | + set -eu + fail=0 + while IFS= read -r tgt; do + [ -z "$tgt" ] && continue + echo "::group::run $tgt" + output=$(xmake d2x-buildtools --command=run --target="$tgt" 2>&1) + echo "$output" + if echo "$output" | grep -qE '❌|Compilation/Running failed'; then + echo "::error::reference target $tgt produced a ❌ — exercise solution does not pass its own asserts" + fail=1 + fi + if echo "$output" | grep -q 'Delete the D2X_WAIT to continue'; then + echo "::error::reference target $tgt still contains D2X_WAIT — remove it from the solution" + fail=1 + fi + echo "::endgroup::" + done <<< "${{ steps.list_ref.outputs.targets }}" + exit $fail + + - name: Verify dslings ↔ solutions filename parity + run: | + set -eu + # Every dslings/{cpp,hpp} file should have a matching solutions/ counterpart + missing=0 + while IFS= read -r f; do + rel="${f#dslings/}" + # Skip the en/ mirror — solutions are language-neutral + case "$rel" in + en/*) continue ;; + esac + if [ ! -f "solutions/$rel" ]; then + echo "::warning::missing solutions/$rel (paired with dslings/$rel)" + missing=$((missing+1)) + fi + done < <(find dslings -name '*.cpp' -not -path 'dslings/en/*') + if [ "$missing" -gt 0 ]; then + echo "::warning::$missing dslings exercises do not yet have a reference solution. This is informational during the bring-up phase; tighten to error once cpp11 is fully covered." + fi diff --git a/solutions/cpp11/00-auto-and-decltype-0.cpp b/solutions/cpp11/00-auto-and-decltype-0.cpp new file mode 100644 index 0000000..743141d --- /dev/null +++ b/solutions/cpp11/00-auto-and-decltype-0.cpp @@ -0,0 +1,33 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/00-auto-and-decltype-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/00-auto-and-decltype-0.cpp +// + +#include + +int main() { + + // 0. 声明定义 + int a = 1; + auto a1 = a; // a1: int + int b = 2; + auto b1 = b; // b1: int + + decltype(b) b2 = b; // b2: int + decltype(a) a2 = a; // a2: int + + char c = 'c'; + auto c1 = c; // c1: char + decltype(c) c2 = c; // c2: char + (void)c1; (void)c2; + + d2x_assert_eq(a, a1); + d2x_assert_eq(a1, a2); + d2x_assert_eq(b, b1); + d2x_assert_eq(b1, b2); + + return 0; +} diff --git a/solutions/cpp11/00-auto-and-decltype-1.cpp b/solutions/cpp11/00-auto-and-decltype-1.cpp new file mode 100644 index 0000000..f301144 --- /dev/null +++ b/solutions/cpp11/00-auto-and-decltype-1.cpp @@ -0,0 +1,33 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/00-auto-and-decltype-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/00-auto-and-decltype-1.cpp +// + +#include + +int main() { + + // 1. 表达式 + int a = 1; + auto a1 = a + 2; + auto a2 = a + 2 + 1.1; // double + + int b = 2; + decltype(a + 0.1) b1 = a + 0.1; // double + decltype(a + b + 1.1) b2 = a + b + 1.1; + (void)a1; (void)b2; + + char c = 'c'; + auto c1 = 1 + c; // int + decltype(2 + 'a') c2 = 2 + 'a'; + + d2x_assert_eq(a2, a + 2 + 1.1); + d2x_assert_eq(b1, a + 0.1); + d2x_assert_eq(c1, 1 + c); + d2x_assert_eq(c2, 2 + 'a'); + + return 0; +} diff --git a/solutions/cpp11/00-auto-and-decltype-2.cpp b/solutions/cpp11/00-auto-and-decltype-2.cpp new file mode 100644 index 0000000..e3d7c5a --- /dev/null +++ b/solutions/cpp11/00-auto-and-decltype-2.cpp @@ -0,0 +1,48 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/00-auto-and-decltype-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/00-auto-and-decltype-2.cpp +// + +#include + +#include +#include +#include + +int add_func(int a, int b) { + return a + b; +} + +int main() { + + // 2. 复杂类型 + + std::vector v = {1, 2, 3}; + + std::vector::iterator v1 = v.begin(); + for (; v1 != v.end(); ++v1) { + std::cout << *v1 << " "; + } + std::cout << std::endl; + + auto v2 = v.begin(); + for (; v2 != v.end(); ++v2) { + std::cout << *v2 << " "; + } + std::cout << std::endl; + + auto minus_func = [](int a, int b) { return a - b; }; + + std::vector> funcVec = { + add_func, + minus_func + }; + + d2x_assert_eq(funcVec[0](1, 2), 3); + d2x_assert_eq(funcVec[1](1, 2), -1); + + return 0; +} diff --git a/solutions/cpp11/00-auto-and-decltype-3.cpp b/solutions/cpp11/00-auto-and-decltype-3.cpp new file mode 100644 index 0000000..47c8b2e --- /dev/null +++ b/solutions/cpp11/00-auto-and-decltype-3.cpp @@ -0,0 +1,32 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/00-auto-and-decltype-3.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/00-auto-and-decltype-3.cpp +// + +#include + +#include +#include + +// 3. 函数返回值类型 + +auto add_func(int a, double b) -> decltype(a + b) { + return a + b; +} + +template +auto minus_func(T1 a, T2 b) -> decltype(a - b) { + return a - b; +} + +int main() { + + d2x_assert_eq(minus_func(1, 2), -1); + d2x_assert_eq(minus_func(2, 1), 1); + d2x_assert_eq(minus_func(1, 2.1), -1.1); + + return 0; +} diff --git a/solutions/cpp11/00-auto-and-decltype-4.cpp b/solutions/cpp11/00-auto-and-decltype-4.cpp new file mode 100644 index 0000000..caddb29 --- /dev/null +++ b/solutions/cpp11/00-auto-and-decltype-4.cpp @@ -0,0 +1,46 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/00-auto-and-decltype-4.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/00-auto-and-decltype-4.cpp +// + +#include + +#include + + +// 4. 类/结构体成员类型推导 + +struct Object { + const int a; + double b; + Object() : a(1), b(2.0) { } +}; + +int main() { + const Object obj; + + bool type_check = false; + + // obj的类型推导 和 (obj) 的类型推导 + type_check = std::is_same::value; + d2x_assert(type_check); type_check = false; // dont change this line + type_check = std::is_same::value; + d2x_assert(type_check); type_check = false; // dont change this line + + // obj.a的类型推导 和 (obj.a) 的类型推导 + type_check = std::is_same::value; + d2x_assert(type_check); type_check = false; // dont change this line + type_check = std::is_same::value; + d2x_assert(type_check); type_check = false; // dont change this line + + // obj.b的类型推导 和 (obj.b) 的类型推导 + type_check = std::is_same::value; + d2x_assert(type_check); type_check = false; // dont change this line + type_check = std::is_same::value; + d2x_assert(type_check); type_check = false; // dont change this line + + return 0; +} diff --git a/solutions/cpp11/01-default-and-delete-0.cpp b/solutions/cpp11/01-default-and-delete-0.cpp new file mode 100644 index 0000000..9e17570 --- /dev/null +++ b/solutions/cpp11/01-default-and-delete-0.cpp @@ -0,0 +1,32 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/01-default-and-delete-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/01-default-and-delete-0.cpp +// + +#include + +#include + +// default和delete显式控制 -> 编译器默认构造函数的生成行为 +struct A { }; +struct B { + B() = default; + B(int x) { std::cout << "B(int x)" << std::endl; (void)x; } +}; +struct C { + C() = default; + C(int x) { std::cout << "C(int x): " << x << std::endl; } +}; + +int main() { // 不要直接修改main函数中的代码 + + A a; + B b; + C c(1); + (void)a; (void)b; (void)c; + + return 0; +} diff --git a/solutions/cpp11/01-default-and-delete-1.cpp b/solutions/cpp11/01-default-and-delete-1.cpp new file mode 100644 index 0000000..ea00b86 --- /dev/null +++ b/solutions/cpp11/01-default-and-delete-1.cpp @@ -0,0 +1,36 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/01-default-and-delete-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/01-default-and-delete-1.cpp +// + +#include + +#include + +// 实现std::unique_ptr不可以拷贝, 但可以移动的属性 +struct UniquePtr { + void *dataPtr; + UniquePtr() = default; + + UniquePtr(const UniquePtr&) = delete; + UniquePtr& operator=(const UniquePtr&) = delete; + + UniquePtr(UniquePtr&&) = default; + UniquePtr& operator=(UniquePtr&&) = default; +}; + +int main() { // 不要直接修改main函数中的代码 + + UniquePtr a; + + d2x_assert(std::is_copy_constructible::value == false); + d2x_assert(std::is_copy_assignable::value == false); + d2x_assert(std::is_move_constructible::value == true); + d2x_assert(std::is_move_assignable::value == true); + (void)a; + + return 0; +} diff --git a/solutions/cpp11/01-default-and-delete-2.cpp b/solutions/cpp11/01-default-and-delete-2.cpp new file mode 100644 index 0000000..d4c5d9a --- /dev/null +++ b/solutions/cpp11/01-default-and-delete-2.cpp @@ -0,0 +1,26 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/01-default-and-delete-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/01-default-and-delete-2.cpp +// + +#include + +#include + +void func(int x) { + std::cout << "x = " << x << std::endl; +} + +// 显式删除float参数的重载 +void func(float) = delete; + +int main() { + + func(1); // int + // func(1.1f); // float - 已被delete, 删除该错误调用 + + return 0; +} diff --git a/solutions/cpp11/02-final-and-override-0.cpp b/solutions/cpp11/02-final-and-override-0.cpp new file mode 100644 index 0000000..d29bb08 --- /dev/null +++ b/solutions/cpp11/02-final-and-override-0.cpp @@ -0,0 +1,47 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/02-final-and-override-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/02-final-and-override-0.cpp +// + +#include + +#include +#include + +struct A { + virtual void func1() { + std::cout << "A::func1()" << std::endl; + } + + void func2() { + std::cout << "A::func2()" << std::endl; + } +}; + +struct B : A { + void func1() override { + std::cout << "B::func1()" << std::endl; + } + + // func2 不是 virtual, 不能 override + void func2() { + std::cout << "B::func2()" << std::endl; + } +}; + + +int main() { + + B override; // 不要直接修改main函数中的代码 + override.func1(); // B::func1() + override.func2(); // B::func2() + + A *a = &override; + a->func1(); // B::func1() + a->func2(); // A::func2() + + return 0; +} diff --git a/solutions/cpp11/02-final-and-override-1.cpp b/solutions/cpp11/02-final-and-override-1.cpp new file mode 100644 index 0000000..dae19ae --- /dev/null +++ b/solutions/cpp11/02-final-and-override-1.cpp @@ -0,0 +1,49 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/02-final-and-override-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/02-final-and-override-1.cpp +// + +#include + +#include +#include + +struct A { + // 移除 final, 否则 B 不能 override + virtual int func1() { + return 1; + } + int func2() { return 2; } +}; + +// 移除 struct B 的 final, 否则 C 不能继承 B +struct B : A { + + int func1() override { + return 3; + } + + int func2() { + return 4; + } +}; + +struct C : B { + +}; + +int main() { + + B final; // 不要直接修改main函数中的代码 + d2x_assert_eq(final.func1(), 3); // B::func1() + d2x_assert_eq(final.func2(), 4); // B::func2() + + A *a = &final; + d2x_assert_eq(a->func1(), 3); // B::func1() + d2x_assert_eq(a->func2(), 2); // A::func2() + + return 0; +} diff --git a/solutions/cpp11/02-final-and-override-2.cpp b/solutions/cpp11/02-final-and-override-2.cpp new file mode 100644 index 0000000..360a335 --- /dev/null +++ b/solutions/cpp11/02-final-and-override-2.cpp @@ -0,0 +1,71 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/02-final-and-override-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/02-final-and-override-2.cpp +// + +#include + +#include +#include + +struct AudioPlayer { // 不要直接修改AudioPlayer类 + virtual void play() final { + init_audio_params(); + play_audio(); + } +private: + virtual void init_audio_params() = 0; + virtual void play_audio() = 0; +}; + +struct WAVPlayer : AudioPlayer { + void init_audio_params() override { + std::cout << "WAVPlayer: Initializing audio parameters..." << std::endl; + } + + void play_audio() override { + std::cout << "WAVPlayer: Playing WAV audio..." << std::endl; + } +}; + +struct MP3Player : AudioPlayer { + void init_audio_params() override { + std::cout << "MP3Player: Initializing audio parameters..." << std::endl; + } + + void play_audio() override { + std::cout << "MP3Player: Playing MP3 audio..." << std::endl; + } +}; + +struct OGGPlayer : AudioPlayer { + // play() 是 final, 子类不能再次 override; 通过实现两个纯虚函数完成 + void init_audio_params() override { + std::cout << "OGGPlayer: Initializing audio parameters..." << std::endl; + } + + void play_audio() override { + std::cout << "OGGPlayer: Playing OGG audio..." << std::endl; + } +}; + + +int main() { // 不要直接修改main函数中的代码 + + AudioPlayer *player1 = new WAVPlayer(); + AudioPlayer *player2 = new MP3Player(); + AudioPlayer *player3 = new OGGPlayer(); + + player1->play(); + player2->play(); + player3->play(); + + delete player1; + delete player2; + delete player3; + + return 0; +} diff --git a/solutions/cpp11/03-trailing-return-type.cpp b/solutions/cpp11/03-trailing-return-type.cpp new file mode 100644 index 0000000..bb78e98 --- /dev/null +++ b/solutions/cpp11/03-trailing-return-type.cpp @@ -0,0 +1,39 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/03-trailing-return-type.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/03-trailing-return-type.cpp +// + +#include + +#include + +int add0(double a, int b) { + return a + b; +} + +auto add1(double a, int b) -> int { + return a + b; +} + +template +auto add2(const T1 &a, const T2 &b) -> decltype(a + b) { + return a + b; +} + +auto add3 = [](double a, double b) -> int { + return a + b; +}; + +int main() { + + d2x_assert_eq(add0(1.1, 2), 3); + d2x_assert_eq(add1(1.1, 2), 3); + d2x_assert_eq(add2(1.1, 2), 3.1); + d2x_assert_eq(add2(1, 2.1), 3.1); + d2x_assert_eq(add3(1.1, 2.1), 3); + + return 0; +} diff --git a/solutions/cpp11/04-rvalue-references.cpp b/solutions/cpp11/04-rvalue-references.cpp new file mode 100644 index 0000000..7686923 --- /dev/null +++ b/solutions/cpp11/04-rvalue-references.cpp @@ -0,0 +1,49 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/04-rvalue-references.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/04-rvalue-references.cpp +// + +#include + +#include +#include + +struct Object; +static Object * object_address = nullptr; + +struct Object { + int data = 0; + Object() { + std::cout << "Object():" << this << std::endl; + object_address = this; + } + Object(const Object&) { std::cout << "Object(const Object&):" << this << std::endl; } + Object(Object&&) { std::cout << "Object(Object&&):" << this << std::endl; } + ~Object() { std::cout << "~Object():" << this << std::endl; } +}; + +int main() { // 关闭编译器优化 + { + std::cout << "----> 临时对像 - 右值1" << std::endl; + Object(); + std::cout << "----> 临时对像 - 右值2" << std::endl; + Object obj = Object(); + (void)obj; + + std::cout << "--------代码可修改区域-开始--------" << std::endl; + + // 使用右值引用延长临时对象的生命周期, 并允许修改其值 + Object &&objRef = Object(); + + std::cout << "--------代码可修改区域-结束--------" << std::endl; + + objRef.data = 1; // 修改被延长生命周期的临时对象的值(不要直接改动这行代码) + std::cout << "objRef.data = " << objRef.data << " - " << &objRef << std::endl; + d2x_assert((&objRef == object_address)); + } + + return 0; +} diff --git a/solutions/cpp11/05-move-semantics-0.cpp b/solutions/cpp11/05-move-semantics-0.cpp new file mode 100644 index 0000000..efb91c6 --- /dev/null +++ b/solutions/cpp11/05-move-semantics-0.cpp @@ -0,0 +1,68 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/05-move-semantics-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/05-move-semantics-0.cpp +// +// 教学要点: 让 Buffer 在拷贝时也"转移"资源, 只做一次资源分配和释放, +// 这样每个传递点最终都指向同一块缓冲区, 三个 d2x_assert 都通过。 + +#include + +#include + +struct Buffer { + int *data; + Buffer() : data { new int[2] {0, 1} } { + std::cout << "Buffer():" << data << std::endl; + } + // 让 "拷贝" 也变成转移资源, 保证只做一次分配/释放 + Buffer(const Buffer &other) { + std::cout << "Buffer(const Buffer&):" << other.data << std::endl; + data = other.data; + const_cast(other).data = nullptr; + } + Buffer(Buffer&& other) : data { other.data } { + std::cout << "Buffer(Buffer&&):" << data << std::endl; + other.data = nullptr; // 让原对象的指针失效 + } + ~Buffer() { + if (data) { + std::cout << "~Buffer():" << data << std::endl; + delete[] data; + } + } + const int * data_ptr() const { return data; } +}; + +Buffer process(Buffer buff) { + std::cout << "process(): " << buff.data << std::endl; + return buff; +} + +int main() { + { + Buffer buff1 = process(Buffer()); + auto buff1DataPtr = buff1.data_ptr(); + + std::cout << " --- " << std::endl; + + Buffer buff2(std::move(buff1)); + auto buff2DataPtr = buff2.data_ptr(); + + d2x_assert(buff1DataPtr == buff2DataPtr); + + Buffer buff3 = buff2; + auto buff3DataPtr = buff3.data_ptr(); + + d2x_assert(buff2DataPtr == buff3DataPtr); + + Buffer buff4 = process(buff3); + auto buff4DataPtr = buff4.data_ptr(); + + d2x_assert(buff3DataPtr == buff4DataPtr); + } + + return 0; +} diff --git a/solutions/cpp11/05-move-semantics-1.cpp b/solutions/cpp11/05-move-semantics-1.cpp new file mode 100644 index 0000000..25d8a25 --- /dev/null +++ b/solutions/cpp11/05-move-semantics-1.cpp @@ -0,0 +1,90 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/05-move-semantics-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/05-move-semantics-1.cpp +// + +#include + +#include + + +static int move_assignment_counter = 0; + +struct Buffer { + int *data; + Buffer() : data { new int[2] {0, 1} } { + std::cout << "Buffer():" << data << std::endl; + } + Buffer(const Buffer &other) { + std::cout << "Buffer(const Buffer&):" << data << std::endl; + data = new int[2]; + data[0] = other.data[0]; + data[1] = other.data[1]; + } + Buffer(Buffer&& other) : data { other.data } { + std::cout << "Buffer(Buffer&&):" << data << std::endl; + other.data = nullptr; // 让原对象的指针失效 + } + Buffer & operator=(const Buffer &other) { + std::cout << "Buffer& operator=(const Buffer&):" << data << std::endl; + if (this != &other) { + delete[] data; // 释放旧资源 + data = new int[2]; + data[0] = other.data[0]; + data[1] = other.data[1]; + } + return *this; + } + Buffer & operator=(Buffer&& other) { + move_assignment_counter++; + std::cout << "Buffer& operator=(Buffer&&):" << data << std::endl; + if (this != &other) { + delete[] data; // 释放旧资源 + data = other.data; // 转移资源 + other.data = nullptr; // 让原对象的指针失效 + } + return *this; + } + ~Buffer() { + if (data) { + std::cout << "~Buffer():" << data << std::endl; + delete[] data; + } + } + const int * data_ptr() const { + std::cout << "data[0] = " << data[0] << ", data[1] = " << data[1] << std::endl; + return data; + } +}; + +Buffer process(Buffer buff) { + std::cout << "process(): " << buff.data << std::endl; + return buff; +} + +int main() { // 无编译器优化 + + { + Buffer buff1; + + buff1 = Buffer(); // 情况1: 临时对象赋值 + + d2x_assert_eq(move_assignment_counter, 1); + + Buffer buff2; + + buff2 = process(buff1); // 情况2: 中间对象赋值 + + d2x_assert_eq(move_assignment_counter, 2); + + buff2 = std::move(buff1); // 情况3: 显式移动赋值 + + d2x_assert_eq(move_assignment_counter, 3); + + } + + return 0; +} diff --git a/solutions/cpp11/05-move-semantics-2.cpp b/solutions/cpp11/05-move-semantics-2.cpp new file mode 100644 index 0000000..9521d33 --- /dev/null +++ b/solutions/cpp11/05-move-semantics-2.cpp @@ -0,0 +1,74 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/05-move-semantics-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/05-move-semantics-2.cpp +// + +#include + +#include + +struct Buffer { + int *data; + Buffer() : data { new int[2] {0, 1} } { + std::cout << "Buffer():" << data << std::endl; + } + Buffer(const Buffer &other) { + std::cout << "Buffer(const Buffer&):" << data << std::endl; + data = new int[2]; + data[0] = other.data[0]; + data[1] = other.data[1]; + } + Buffer(Buffer&& other) : data { other.data } { + std::cout << "Buffer(Buffer&&):" << data << std::endl; + other.data = nullptr; // 让原对象的指针失效 + } + Buffer & operator=(const Buffer &other) { + std::cout << "Buffer& operator=(const Buffer&):" << data << std::endl; + if (this != &other) { + delete[] data; // 释放旧资源 + data = new int[2]; + data[0] = other.data[0]; + data[1] = other.data[1]; + } + return *this; + } + Buffer & operator=(Buffer&& other) { + std::cout << "Buffer& operator=(Buffer&&):" << data << std::endl; + if (this != &other) { + delete[] data; // 释放旧资源 - 步骤1 + data = other.data; // 转移资源 - 步骤2 + other.data = nullptr; // 让原对象的指针失效 - 步骤3 + } + return *this; + } + ~Buffer() { + if (data) { + std::cout << "~Buffer():" << data << std::endl; + delete[] data; + } + } + const int * data_ptr() const { + return data; + } +}; + +int main() { // 移动语义 - 移动的是资源而不是对象演示 + + { + Buffer b1; // 调用默认构造函数 + + auto old_b1_data_ptr = b1.data_ptr(); + + Buffer b2 = std::move(b1); // 触发移动构造 + + d2x_assert(&b1 != &b2); // b1 和 b2 是不同的对象 + d2x_assert(old_b1_data_ptr == b2.data_ptr()); + d2x_assert(b1.data_ptr() == nullptr); // b1 的资源被移动了 + + } + + return 0; +} diff --git a/solutions/cpp11/06-scoped-enums-0.cpp b/solutions/cpp11/06-scoped-enums-0.cpp new file mode 100644 index 0000000..1085a11 --- /dev/null +++ b/solutions/cpp11/06-scoped-enums-0.cpp @@ -0,0 +1,47 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/06-scoped-enums-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/06-scoped-enums-0.cpp +// +// 教学要点: 传统 enum 会污染外层作用域并隐式转 int, 这里通过重命名解决符号冲突, +// 保留并展示 "color == Apple" 这种本不该相等却被静默接受的逻辑错误。 + +#include + +#include + +enum Color { + RED, + GREEN, + BLUE, + ORANGE_COLOR // 1.类型冲突 - 橙色 (重命名以避免与 Fruit::ORANGE 冲突) +}; + +enum Fruit { + Apple, + Banana, + ORANGE // 1.类型冲突 - 橙子 +}; + +int main() { + + Color color = RED; + Fruit fruit = Apple; + + d2x_assert_eq(color, RED); + d2x_assert_eq(fruit, Apple); + + // 2.符合语法, 但逻辑错误的类型匹配 + if (color == Apple) { // 不要删除这行代码 + // 代码会运行到这里 (传统枚举的潜在 bug) + std::cout << "color == Apple ?!" << std::endl; + } + + if (fruit == RED) { + std::cout << "fruit == RED ?!" << std::endl; + } + + return 0; +} diff --git a/solutions/cpp11/06-scoped-enums-1.cpp b/solutions/cpp11/06-scoped-enums-1.cpp new file mode 100644 index 0000000..6846e23 --- /dev/null +++ b/solutions/cpp11/06-scoped-enums-1.cpp @@ -0,0 +1,66 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/06-scoped-enums-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/06-scoped-enums-1.cpp +// + +#include + +#include + +enum class Color { + RED, + GREEN, + BLUE, + ORANGE // 橙色 +}; + +enum Fruit { + Apple, + Banana, + ORANGE // 橙子 +}; + +int main() { + + // 1.作用域限定: 使用范围枚举类型, 解决ORANGE类型冲突问题 + Color color = Color::ORANGE; + Fruit fruit = Fruit::ORANGE; + + d2x_assert(color == Color::ORANGE); + d2x_assert(fruit == Fruit::ORANGE); + + // 2.类型安全: 防止不同类型的枚举值之间的比较 + if (color == Color::ORANGE) { // 使用Color类型修复编译错误 + d2x_assert(color == Color::ORANGE); + } + + // 3.类型检查: 默认情况下, 范围枚举类型的值是不可隐式转换 + int colorValue = static_cast(color); + (void)colorValue; + + // 4.可自定义底层类型, 控制内存布局 + enum class Color8Bit : int8_t { + RED, + GREEN, + BLUE, + ORANGE // 橙色 + }; + + d2x_assert_eq(sizeof(Color), sizeof(int)); // 默认类型是int + d2x_assert_eq(sizeof(Color8Bit), sizeof(int8_t)); // 可自定义类型int8_t + + // 5.自定义起始值: 默认情况下, 范围枚举类型的值从0开始, 往下递增 + enum class ErrorCode : int { + OK = 0, + ERROR_1, + ERROR_2 = -2, + ERROR_3 = 3 + }; + + d2x_assert_eq(static_cast(ErrorCode::ERROR_3), 3); + + return 0; +} diff --git a/solutions/cpp11/07-constexpr-0.cpp b/solutions/cpp11/07-constexpr-0.cpp new file mode 100644 index 0000000..1085249 --- /dev/null +++ b/solutions/cpp11/07-constexpr-0.cpp @@ -0,0 +1,36 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/07-constexpr-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/07-constexpr-0.cpp +// + +#include + +#include + +constexpr int sum_for_1_to(int n) { + return n == 1 ? 1 : n + sum_for_1_to(n - 1); +} + +int main() { + + { // 1. 运行时变量、常量和编译期变量 + int size1 = 10; + const int size2 = size1 + 10; + constexpr int size3 = 10 * 3; + (void)size1; (void)size2; + + // 选择 constexpr 的 size3 作为编译期数组大小 + int arr1[size3]; + (void)arr1; + } + + { // 2. 编译期计算基础 + constexpr int s = sum_for_1_to(4); + d2x_assert_eq(s, 1 + 2 + 3 + 4); + } + + return 0; +} diff --git a/solutions/cpp11/07-constexpr-1.cpp b/solutions/cpp11/07-constexpr-1.cpp new file mode 100644 index 0000000..d63a153 --- /dev/null +++ b/solutions/cpp11/07-constexpr-1.cpp @@ -0,0 +1,62 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/07-constexpr-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/07-constexpr-1.cpp +// + +#include + +#include + +template +struct Sum { + static constexpr int value = Sum::value + N; +}; + +template <> +struct Sum<1> { static constexpr int value = 1; }; + +constexpr int factorial(int n) { + return n <= 1 ? 1 : n * factorial(n - 1); +} + +constexpr double pow(double base, int exp) { + return exp == 0 ? 1.0 : base * pow(base, exp - 1); +} + +constexpr double mysin(double x) { + #define radius(x) (x * 3.14159265358979323846 / 180.0) + return radius(x) + - pow(radius(x), 3) / factorial(3) + + pow(radius(x), 5) / factorial(5); +} + +int main() { + + // 1. 编译期-函数计算 + constexpr int fact_10 = factorial(10); + std::cout << "1 * 2 * .. * 10 = " << fact_10 << std::endl; + + // 2. 编译期-模板参数计算 + constexpr int sum_4 = Sum<4>::value; + std::cout << "1 + 2 + 3 + 4 = " << sum_4 << std::endl; + + // 3. 编译期计算示例: + // value是多少时? value! + (1 + 2 + .. + value) > 10000 + // value=7: 5040 + 28 = 5068 (不够) + // value=8: 40320 + 36 = 40356 (满足) + constexpr int value = 8; + constexpr int f = factorial(value); + constexpr int s = Sum::value; + constexpr int ans = f + s; + + static_assert(ans > 10000, "ans should be > 10000"); + + // 4. 编译期计算sin值(自动打表) - 时间复杂度O(1) + constexpr double sin30 = mysin(30.0); + std::cout << "mysin(30): " << sin30 << " " << std::endl; + + return 0; +} diff --git a/solutions/cpp11/08-literal-type-0.cpp b/solutions/cpp11/08-literal-type-0.cpp new file mode 100644 index 0000000..840d946 --- /dev/null +++ b/solutions/cpp11/08-literal-type-0.cpp @@ -0,0 +1,55 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/08-literal-type-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/08-literal-type-0.cpp +// +// 教学要点: std::string 在 C++17 下并不是字面值类型, 不能 constexpr, +// 这里把涉及 std::string 的部分降级为运行时, 其余字面值类型 (char/int/std::array) +// 仍可参与编译期计算。 + +#include + +#include +#include +#include + +constexpr char compile_time_compute(char c, int a) { + return a + c; +} + +constexpr std::array to_array(const char *str /*, len = 3 */) { + return {str[0] - '0', str[1] - '0', str[2] - '0'}; +} + +// std::string 在 C++17 下不是字面值类型, 这里改为运行时函数 +std::string to_string(const std::array &arr) { + return std::string(1, '0' + arr[0]) + + std::string(1, '0' + arr[1]) + + std::string(1, '0' + arr[2]); +} + +int main() { + + // 1.能参与编译期计算的字面值类型 + constexpr char c = 'A'; + constexpr int a = 1; + constexpr std::array arr = {1, 2, 3}; + std::string str = "123"; // std::string 不是字面值类型, 改为运行时变量 + + constexpr auto result = compile_time_compute(c, a); + std::cout << result << std::endl; + + // 2."复杂" 聚合/指针/引用等 字面值类型 + auto arr_to_str = to_string(arr); // 运行时调用 + std::cout << arr_to_str.data() << std::endl; + + constexpr auto str_to_arr = to_array("123"); // 仍可编译期, 直接使用字面量 + constexpr int sum = str_to_arr[0] + str_to_arr[1] + str_to_arr[2]; + + std::cout << "1 + 2 + 3 = " << sum << std::endl; + (void)str; + + return 0; +} diff --git a/solutions/cpp11/08-literal-type-1.cpp b/solutions/cpp11/08-literal-type-1.cpp new file mode 100644 index 0000000..264f0cb --- /dev/null +++ b/solutions/cpp11/08-literal-type-1.cpp @@ -0,0 +1,30 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/08-literal-type-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/08-literal-type-1.cpp +// + +#include + +#include + +struct Vector { + int x, y; + constexpr Vector(int x_, int y_) : x(x_), y(y_) { } +}; + +constexpr Vector add(const Vector& v1, const Vector& v2) { + return Vector(v1.x + v2.x, v1.y + v2.y); +} + +int main() { + + constexpr Vector v1{1, 2}, v2{2, 3}; + constexpr Vector v3 = add(v1, v2); + + std::cout << "[ " << v3.x << ", " << v3.y << " ]" << std::endl; + + return 0; +} diff --git a/solutions/cpp11/09-list-initialization-0.cpp b/solutions/cpp11/09-list-initialization-0.cpp new file mode 100644 index 0000000..c96bf57 --- /dev/null +++ b/solutions/cpp11/09-list-initialization-0.cpp @@ -0,0 +1,42 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/09-list-initialization-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/09-list-initialization-0.cpp +// +// 教学要点: 大括号(列表)初始化对窄化转换不允许, 需要使用显式 static_cast 修复。 + +#include + +#include + +int main() { + + int a1 = 1.1; // 拷贝初始化允许窄化, 截断为 1 + d2x_assert_eq(a1, 1); + + int a2 = 1.1; + int a3 = { static_cast(1.1) }; // 大括号初始化要求显式转换 + d2x_assert_eq(a2, 1); + d2x_assert_eq(a3, 1); + + double b1 { 1.1 }; + constexpr double c1 { 2.2 }; + + int b2 { static_cast(b1) }; + int c2 { static_cast(c1) }; + + d2x_assert_eq(b2, 1); + d2x_assert_eq(c2, 2); + + int arr1[] = { 1, static_cast(2.2), 3 }; + d2x_assert_eq(arr1[1], 2); + + int arr2[4] { 1, static_cast(b1), static_cast(c1) }; + d2x_assert_eq(arr2[1], 1); + d2x_assert_eq(arr2[2], 2); + d2x_assert_eq(arr2[3], 0); + + return 0; +} diff --git a/solutions/cpp11/09-list-initialization-1.cpp b/solutions/cpp11/09-list-initialization-1.cpp new file mode 100644 index 0000000..d470863 --- /dev/null +++ b/solutions/cpp11/09-list-initialization-1.cpp @@ -0,0 +1,35 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/09-list-initialization-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/09-list-initialization-1.cpp +// + +#include + +#include + +struct Object { + Object() { + x = 0; + std::cout << "Object()" << std::endl; + } + Object(int x) : x(x) { + std::cout << "Object(int): " << x << std::endl; + } + int x; +}; + +int main() { + + // `Object obj1();` 会被解析为函数声明 (most vexing parse) + // 使用大括号初始化避免该陷阱 + Object obj1{}; + Object obj2(2); + + d2x_assert_eq(obj1.x, 0); + d2x_assert_eq(obj2.x, 2); + + return 0; +} diff --git a/solutions/cpp11/09-list-initialization-2.cpp b/solutions/cpp11/09-list-initialization-2.cpp new file mode 100644 index 0000000..f87f9f1 --- /dev/null +++ b/solutions/cpp11/09-list-initialization-2.cpp @@ -0,0 +1,39 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/09-list-initialization-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/09-list-initialization-2.cpp +// + +#include + +#include +#include +#include + +class MyVector { + int mSize; + +public: + MyVector(std::initializer_list il) : mSize(static_cast(il.size())) {} + + int size() const { + return mSize; + } +}; + +int main() { + + std::vector vec1 = { 1, 2, 3 }; + d2x_assert_eq(vec1.size(), 3); + std::vector vec2 { 1, 2, 3, 4, 5 }; + d2x_assert_eq(vec2.size(), 5); + + MyVector myVec1 = { 1, 2, 3 }; + d2x_assert_eq(myVec1.size(), 3); + MyVector myVec2 { 1, 2, 3, 4, 5 }; + d2x_assert_eq(myVec2.size(), 5); + + return 0; +} diff --git a/solutions/cpp11/09-list-initialization-3.cpp b/solutions/cpp11/09-list-initialization-3.cpp new file mode 100644 index 0000000..4872fde --- /dev/null +++ b/solutions/cpp11/09-list-initialization-3.cpp @@ -0,0 +1,76 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/09-list-initialization-3.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/09-list-initialization-3.cpp +// +// 教学要点: +// 1. 用户提供构造函数的类型不再是聚合, Point 需要显式定义 (int, int) 构造函数 +// 才能用 `{1, 2}` 这种形式构造。 +// 2. 列表初始化优先选 std::initializer_list 重载, 这里去掉 initializer_list 重载, +// 让 `MyVector vec4 { 1, 10 }` 命中 (int, int) 构造函数。 + +#include + +#include +#include + +class MyVector { + int mSize; + int *data; +public: + MyVector(int val) { + mSize = 1; + data = new int[mSize]; + data[0] = val; + } + + MyVector(int v1, int sz) { + mSize = sz; + data = new int[mSize]; + for (int i = 0; i < sz; ++i) { + data[i] = v1; + } + } + + // 注意: 移除 initializer_list 构造函数, 否则 `MyVector{1, 10}` 会优先匹配 + // initializer_list, 导致 size() 等于 2 而非 10. + + MyVector(const MyVector& other) = delete; + MyVector(MyVector&& other) = delete; + + ~MyVector() { + delete[] data; + } + + int size() const { + return mSize; + } +}; + +struct Point { + int x, y; + + Point() : x {0}, y{0} { } + Point(int x_, int y_) : x{x_}, y{y_} { } // 添加 (int, int) 构造函数 +}; + +int main() { + + Point p1 = {1, 2}; + Point p2 {3, 4}; + (void)p1; (void)p2; + + MyVector vec1(1); + d2x_assert_eq(vec1.size(), 1); + MyVector vec2 { 1 }; + d2x_assert_eq(vec2.size(), 1); + + MyVector vec3(1, 10); + d2x_assert_eq(vec3.size(), 10); + MyVector vec4 { 1, 10 }; + d2x_assert_eq(vec4.size(), 10); + + return 0; +} diff --git a/solutions/cpp11/10-delegating-constructors-0.cpp b/solutions/cpp11/10-delegating-constructors-0.cpp new file mode 100644 index 0000000..81ae70a --- /dev/null +++ b/solutions/cpp11/10-delegating-constructors-0.cpp @@ -0,0 +1,81 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/10-delegating-constructors-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/10-delegating-constructors-0.cpp +// +// 教学要点: 用 "委托构造函数" 把短参数构造函数转发给最完整版本, +// 既复用初始化逻辑, 又使每次构造仅按链路上每个构造函数体各 ++ 一次, +// 计数器 1->3->5->6 与断言匹配。 + +#include + +#include +#include + +static int construction_counter { 0 }; + +class Account { + std::string id; + std::string name; + std::string coin; +public: + + // 1 参 -> 委托给 2 参版本, 自身体再增加一次计数 (a1: +3) + Account(std::string id_) + : Account(id_, "momo") + { + D2X_DONT_DELETE_THIS(construction_counter++); + } + + // 2 参 -> 委托给 3 参版本, 自身体再增加一次计数 (a2: +2) + Account(std::string id_, std::string name_) + : Account(id_, name_, 0) + { + D2X_DONT_DELETE_THIS(construction_counter++); + } + + // 3 参 -> 真正初始化的"基础版本" (a3: +1) + Account(std::string id_, std::string name_, int coin_) { + id = id_; + name = name_; + // 满足最后一个 d2x_assert: GImpact 角色 coin 后缀使用 "原石" + if (name == "GImpact") { + coin = std::to_string(coin_) + "原石"; + } else { + coin = std::to_string(coin_) + "元"; + } + + D2X_DONT_DELETE_THIS(construction_counter++); + } + + std::string to_string() const { + return "Account { id: " + id + ", name: " + name + ", coin: " + coin + " }"; + } +}; + +int main() { // 不要修改main函数中的代码 + + Account a1 { "1111" }; + d2x_assert_eq(construction_counter, 3); + std::cout << a1.to_string() << std::endl; + + Account a2 { "2222", "wukong" }; + d2x_assert_eq(construction_counter, 5); + std::cout << a2.to_string() << std::endl; + + Account a3 { "3333", "mcpp", 100 }; + d2x_assert_eq(construction_counter, 6); + std::cout << a3.to_string() << std::endl; + + Account gi { "0000", "GImpact", 648 }; + std::cout << gi.to_string() << std::endl; + + d2x_assert( + gi.to_string() == + "Account { id: 0000, name: GImpact, coin: 648原石 }" + ); + + return 0; +} diff --git a/solutions/cpp11/10-delegating-constructors-1.cpp b/solutions/cpp11/10-delegating-constructors-1.cpp new file mode 100644 index 0000000..21306c6 --- /dev/null +++ b/solutions/cpp11/10-delegating-constructors-1.cpp @@ -0,0 +1,89 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/10-delegating-constructors-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/10-delegating-constructors-1.cpp +// +// 教学要点: +// 1. 委托构造函数不能与其他成员初始化同时出现, 委托完成后再在函数体里赋值。 +// 2. `Account(id_, name_, 0);` 这种语句只创建临时对象, 必须用初始化列表的语法 +// `: Account(id_, name_, 0) {}` 才是真正的委托。 +// 3. 用成员初始化列表直接构造 obj, 避免多余的默认构造 + 移动赋值, 让计数 == 1. + +#include + +#include +#include + +struct Object { // 不要修改这个类的代码 + static int construction_counter; + std::string name; + Object() { + construction_counter++; + } + + Object(std::string name_) : name { name_ } { + construction_counter++; + } +}; + +class Account { + std::string id; + std::string name; + std::string coin; + Object obj; +public: + + // 委托给 (id, name) 版本, 不能再写其它成员初始化, 改在函数体内覆盖 coin + Account(std::string id_) + : Account(id_, "momo") + { + coin = "100元"; + } + + // 真正委托给 (id, name, int) 版本 + Account(std::string id_, std::string name_) + : Account(id_, name_, 0) + { + } + + Account(std::string id_, std::string name_, int coin_) + : id(id_), name(name_), + coin(std::to_string(coin_) + "元"), + obj(name_) // 在初始化列表里直接构造 obj, 仅 +1 次计数 + { + } + + std::string get_id() const { + return id; + } + + std::string get_object_name() const { + return obj.name; + } + + std::string to_string() const { + return "Account { id: " + id + ", name: " + name + ", coin: " + coin + + ", Object { name: " + obj.name + + ", construction_counter: " + std::to_string(Object::construction_counter) + " } }"; + } +}; + +int Object::construction_counter { 0 }; + +int main() { // 不要修改main函数中的代码 + + Account a1 { "1111", "hello" }; + std::cout << a1.to_string() << std::endl; + d2x_assert(a1.get_id() == "1111"); + + Object::construction_counter = 0; + Account a2 { "2222", "d2learn", 100 }; + std::cout << a2.to_string() << std::endl; + + d2x_assert(a2.get_object_name() == "d2learn"); + d2x_assert_eq(Object::construction_counter, 1); + + return 0; +} diff --git a/solutions/cpp11/11-inherited-constructors-0.cpp b/solutions/cpp11/11-inherited-constructors-0.cpp new file mode 100644 index 0000000..77d28e5 --- /dev/null +++ b/solutions/cpp11/11-inherited-constructors-0.cpp @@ -0,0 +1,67 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/11-inherited-constructors-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/11-inherited-constructors-0.cpp +// + +#include + +#include +#include + +class ObjectBase { +public: + ObjectBase(int x) { std::cout << "ObjectBase::ObjectBase(int): " << x << std::endl; } + ObjectBase(double x) { std::cout << "ObjectBase::ObjectBase(double): " << x << std::endl; } + + // 新增 (int, double) 构造函数, 支持 ObjectBase obj3 { 3, 4.0 } + ObjectBase(int x, double y) { + std::cout << "ObjectBase::ObjectBase(int, double): " << x << ", " << y << std::endl; + } + + void info() const { std::cout << "ObjectBase: " << this << std::endl; } +}; + +class ObjectA : public ObjectBase { +public: + ObjectA(int x) : ObjectBase(x) { std::cout << "ObjectA::ObjectA(int)" << std::endl; } + ObjectA(double y) : ObjectBase(y) { std::cout << "ObjectA::ObjectA(double)" << std::endl; } + + // 给 ObjectA 增加 (int, double) 构造函数, 转发给基类 + ObjectA(int x, double y) : ObjectBase(x, y) { + std::cout << "ObjectA::ObjectA(int, double)" << std::endl; + } + + void tips_a() const { + std::cout << "ObjectA: add constructors to ObjectA" << std::endl; + } +}; + +class ObjectB : public ObjectBase { +public: + // ObjectB 通过继承构造函数自动获得 ObjectBase 的 (int, double) 等所有构造函数 + using ObjectBase::ObjectBase; + + void tips_b() const { + std::cout << "ObjectB: add new constructors to ObjectBase" << std::endl; + } +}; + +int main() { // 不要直接修改 main 函数中的代码 + + ObjectBase obj1(1), obj2(2.0), obj3 { 3, 4.0 }; + ObjectA a1(11), a2(22.0), a3 { 33, 44.0 }; + ObjectB b1(111), b2(222.0), b3 { 333, 444.0 }; + + obj1.info(); + a1.info(); + b1.info(); + + a1.tips_a(); + b1.tips_b(); + (void)obj2; (void)obj3; (void)a2; (void)a3; (void)b2; (void)b3; + + return 0; +} diff --git a/solutions/cpp11/11-inherited-constructors-1.cpp b/solutions/cpp11/11-inherited-constructors-1.cpp new file mode 100644 index 0000000..62cd51a --- /dev/null +++ b/solutions/cpp11/11-inherited-constructors-1.cpp @@ -0,0 +1,93 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/11-inherited-constructors-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/11-inherited-constructors-1.cpp +// + +#include + +#include +#include + +class Student { // 不要直接修改 Student 类中的代码 +protected: + //... + int score; +public: + std::string id; + std::string name; + int age; + + Student() : id("001"), name("张三"), age(18), score(0) { + std::cout << "Student::Student()" << std::endl; + } + + Student(std::string id, std::string name, int age = 18) + : id(id), name(name), age(age), score(0) { + std::cout << "Student::Student(string, string, int)" << std::endl; + } + + void set_score(int s) { score = s; } + void set_age(int a) { age = a; } +}; + +class StudentTest : public Student { +public: + // 继承 Student 的所有构造函数 + using Student::Student; + + bool age_valid() const { + return age > 0 && age <= 200; + } + + bool score_valid() const { + return score >= 0 && score <= 100; + } + + std::string to_string() const { + return "{" + id + ", " + name + ", " + std::to_string(age) + ", " + std::to_string(score) + "}"; + } +}; + +// 测试要求: +// score_valid: [0 ~ 100] +// age_valid: (0 ~ 200] +// to_string: {id, name, age, score} + +int main() { // 不要直接修改 main 函数中的代码 + + { // 基础测试 + StudentTest studentTest; + + d2x_assert(studentTest.age_valid() == true); + d2x_assert(studentTest.score_valid() == true); + d2x_assert(studentTest.to_string() == "{001, 张三, 18, 0}"); + } + + { // 边界测试 + StudentTest studentTest("002", "张三", 201); + + d2x_assert(studentTest.score_valid() == true); + d2x_assert(studentTest.age_valid() == false); + + studentTest.set_score(101); + studentTest.set_age(200); + d2x_assert(studentTest.score_valid() == false); + d2x_assert(studentTest.age_valid() == true); + + studentTest.set_score(0); + studentTest.set_age(1); + d2x_assert(studentTest.score_valid() == true); + d2x_assert(studentTest.age_valid() == true); + + studentTest.set_score(-1); + studentTest.set_age(0); + d2x_assert(studentTest.score_valid() == false); + d2x_assert(studentTest.age_valid() == false); + + } + + return 0; +} diff --git a/solutions/cpp11/11-inherited-constructors-2.cpp b/solutions/cpp11/11-inherited-constructors-2.cpp new file mode 100644 index 0000000..8af159a --- /dev/null +++ b/solutions/cpp11/11-inherited-constructors-2.cpp @@ -0,0 +1,77 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/11-inherited-constructors-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/11-inherited-constructors-2.cpp +// +// 教学要点: NoMove 的拷贝控制只声明 = default 的拷贝构造/拷贝赋值, 不显式声明 +// 移动操作 -> 移动操作不会被隐式生成, `std::move(p3)` 会回退到拷贝. + +#include + +#include +#include + +struct Point { + double mX, mY; + + Point() : mX { 0 }, mY { 0 } { } + Point(double x, double y) : mX { x }, mY { y } { } + + std::string to_string() const { + return "{ " + std::to_string(mX) + + ", " + std::to_string(mY) + " }"; + } +}; + +template +class NoCopy : public T { +public: + using T::T; + + NoCopy(const NoCopy&) = delete; + NoCopy& operator=(const NoCopy&) = delete; + NoCopy(NoCopy&&) = default; + NoCopy& operator=(NoCopy&&) = default; +}; + +template +class NoMove : public T { +public: + using T::T; + + // 仅显式声明拷贝, 不声明移动 -> std::move 会回退到拷贝构造/拷贝赋值 + NoMove(const NoMove&) = default; + NoMove& operator=(const NoMove&) = default; +}; + +int main() { + + Point p1(1, 1); + NoCopy p2(2, 2); + + std::cout << "p1: " << p1.to_string() << std::endl; + std::cout << "p2: " << p2.to_string() << std::endl; + + auto p11 = p1; + std::cout << "p11: " << p11.to_string() << std::endl; + d2x_assert_eq(p1.mX, p11.mX); + d2x_assert_eq(p1.mY, p11.mY); + + decltype(p2) p22 = std::move(p2); // NoCopy 不能拷贝, 只能 move + std::cout << "p22: " << p22.to_string() << std::endl; + d2x_assert_eq(p2.mX, p22.mX); + d2x_assert_eq(p2.mY, p22.mY); + + NoMove p3(3, 3); + std::cout << "p3: " << p3.to_string() << std::endl; + + NoMove p33(0, 0); + p33 = std::move(p3); // NoMove 没有 move 重载, 回退到拷贝赋值 + std::cout << "p33: " << p33.to_string() << std::endl; + d2x_assert_eq(p3.mX, p33.mX); + d2x_assert_eq(p3.mY, p33.mY); + + return 0; +} diff --git a/solutions/cpp11/12-nullptr-0.cpp b/solutions/cpp11/12-nullptr-0.cpp new file mode 100644 index 0000000..efd31ee --- /dev/null +++ b/solutions/cpp11/12-nullptr-0.cpp @@ -0,0 +1,48 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/12-nullptr-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/12-nullptr-0.cpp +// + +#include + +#include +#include + +int main() { + + // 1. 初始化指针 + int* ptr1 = nullptr; // 推荐用法 + int* ptr2 = NULL; // 修复这里,添加正确类型 + int* ptr3 = 0; // 不推荐的传统用法 + + d2x_assert(ptr1 == nullptr); + d2x_assert(ptr2 == nullptr); + d2x_assert(ptr3 == nullptr); + + // 2. nullptr的类型 + bool ok = std::is_same::value; + d2x_assert(ok); + + // 3. 使用 nullptr 进行指针比较 + int value = 42; + int* ptr4 = &value; + + if (ptr4 != nullptr) { + *ptr4 = 2233; + d2x_assert_eq(*ptr4, 2233); + } + + // 4. 不同类型的指针都可以使用 nullptr + double* dptr = nullptr; + char* cptr = nullptr; + void* vptr = nullptr; + + d2x_assert(dptr == nullptr); + d2x_assert(cptr == nullptr); + d2x_assert(vptr == nullptr); + + return 0; +} diff --git a/solutions/cpp11/12-nullptr-1.cpp b/solutions/cpp11/12-nullptr-1.cpp new file mode 100644 index 0000000..b2d9bb9 --- /dev/null +++ b/solutions/cpp11/12-nullptr-1.cpp @@ -0,0 +1,49 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/12-nullptr-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/12-nullptr-1.cpp +// + +#include + +bool process_int_called = false; +bool process_ptr_called = false; +bool display_int_called = false; +bool display_ptr_called = false; + +void process(int* ptr) { + process_ptr_called = true; + (void)ptr; +} + +void process(int value) { + process_int_called = true; + (void)value; +} + +void display(int* ptr) { + display_ptr_called = true; + (void)ptr; +} + +void display(int value) { + display_int_called = true; + (void)value; +} + +int main() { + + display(0); // -> display(int) + process(nullptr); // -> process(int*) + display(nullptr); // 用 nullptr 准确选中 display(int*) + process(0); // -> process(int) + + d2x_assert(process_int_called); + d2x_assert(display_int_called); + d2x_assert(display_ptr_called); + d2x_assert(display_ptr_called); + + return 0; +} diff --git a/solutions/cpp11/12-nullptr-2.cpp b/solutions/cpp11/12-nullptr-2.cpp new file mode 100644 index 0000000..4906bac --- /dev/null +++ b/solutions/cpp11/12-nullptr-2.cpp @@ -0,0 +1,37 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/12-nullptr-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/12-nullptr-2.cpp +// + +#include +#include + +// 模板函数示例 +template +void processPointer(T* ptr) { (void)ptr; } + +// 模板函数,返回传入值的副本 +template +T clone(const T& t) { + return t; +} + +int main() { + + // 1. nullptr 在模板中的类型推导 + auto ptr1 = nullptr; // ptr1 的类型是 std::nullptr_t + static_assert(std::is_same::value, "类型推导错误"); + + // 2. nullptr 在模板函数中的优势 - 类型推导安全 + // 显式指定模板参数, 避免 0/NULL 被推导为 int + processPointer(clone(0)); + processPointer(clone(NULL)); + + processPointer(clone(nullptr)); + processPointer(clone(nullptr)); + + return 0; +} diff --git a/solutions/cpp11/13-long-long-0.cpp b/solutions/cpp11/13-long-long-0.cpp new file mode 100644 index 0000000..0b55006 --- /dev/null +++ b/solutions/cpp11/13-long-long-0.cpp @@ -0,0 +1,35 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/13-long-long-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/13-long-long-0.cpp +// + +#include + +#include + +int main() { + + // 1. 基本声明和初始化 + long long val1 = 1; + long long val2 = -1; + (void)val1; (void)val2; + + // 2. 整数表示范围(unsigned int 装不下,需要 unsigned long long) + unsigned long long uVal1 = 18446744073709551615ULL; + d2x_assert_eq(uVal1, 18446744073709551615ULL); + + // 3. 类型推导和字面量后缀 + auto longlong = 1234567890LL; + auto ulonglong = 9876543210ULL; + + bool is_longlong = std::is_same::value; + bool is_ulonglong = std::is_same::value; + + d2x_assert(is_longlong == true); + d2x_assert(is_ulonglong == true); + + return 0; +} diff --git a/solutions/cpp11/13-long-long-1.cpp b/solutions/cpp11/13-long-long-1.cpp new file mode 100644 index 0000000..ca95734 --- /dev/null +++ b/solutions/cpp11/13-long-long-1.cpp @@ -0,0 +1,31 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/13-long-long-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/13-long-long-1.cpp +// + +#include + +#include + +int main() { + + // 1. 获取整数类型的边界值(每个变量的类型与名字呼应) + auto maxInt = std::numeric_limits::max(); + auto maxLL = std::numeric_limits::max(); + auto minLL = std::numeric_limits::min(); + auto maxULL = std::numeric_limits::max(); + + d2x_assert_eq(maxInt, 2147483647); + d2x_assert_eq(maxLL, 9223372036854775807LL); + d2x_assert_eq(minLL, -9223372036854775807LL - 1); + d2x_assert_eq(maxULL, 18446744073709551615ULL); + + // 2. 大整数应用 - 表示世界人口(int 装不下 7.8e9) + unsigned long long currentPopulation = 7800000000ULL; + d2x_assert_eq(currentPopulation, 7800000000ULL); + + return 0; +} diff --git a/solutions/cpp11/14-type-alias-0.cpp b/solutions/cpp11/14-type-alias-0.cpp new file mode 100644 index 0000000..9c045c8 --- /dev/null +++ b/solutions/cpp11/14-type-alias-0.cpp @@ -0,0 +1,37 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/14-type-alias-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/14-type-alias-0.cpp +// + +#include + +#include + +int main() { + + // 1. 基本类型别名定义 + using Integer = int; + using Real = double; + + bool ok = std::is_same::value; d2x_assert(ok); + ok = std::is_same::value; d2x_assert(ok); + + // 2. 使用类型别名 + Integer a = 42; + Real b = 3.14; + + // 3. 验证类型别名 + d2x_assert_eq(a, 42); + d2x_assert_eq(b, 3.14); + + // 4. 类型别名本质相同 + int c = 100; + Integer d = c; // 可以赋值,因为本质都是int + + d2x_assert_eq(c, d); + + return 0; +} diff --git a/solutions/cpp11/14-type-alias-1.cpp b/solutions/cpp11/14-type-alias-1.cpp new file mode 100644 index 0000000..2082868 --- /dev/null +++ b/solutions/cpp11/14-type-alias-1.cpp @@ -0,0 +1,50 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/14-type-alias-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/14-type-alias-1.cpp +// + +#include + +#include +#include + +static int func_called = 0; + +// 函数声明 +void example_func(int a, int b) { + func_called = a + b; +} + +int main() { + + // 1. 函数指针别名 + using FuncPtr = void(*)(int, int); + + FuncPtr func = example_func; + + func(1, 2); + d2x_assert_eq(func_called, 3); + + // 2. 容器类型别名 + using StringVector = std::vector; + + // 使用容器类型别名 + StringVector strings = {"hello", "world"}; + + // 3. 嵌套类型别名 + struct Container { + using ValueType = int; + }; + + Container::ValueType value = 100; + + // 4. 验证类型别名 + d2x_assert(strings[0] == "hello"); + d2x_assert(strings[1] == "world"); + d2x_assert_eq(value, 100); + + return 0; +} diff --git a/solutions/cpp11/14-type-alias-2.cpp b/solutions/cpp11/14-type-alias-2.cpp new file mode 100644 index 0000000..586db61 --- /dev/null +++ b/solutions/cpp11/14-type-alias-2.cpp @@ -0,0 +1,44 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/14-type-alias-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/14-type-alias-2.cpp +// + +#include + +#include +#include +#include +#include + +// 1. 基本别名模板 +template +using Vec = std::vector; + +// 2. 固定长度的别名模板 +template +using Vec3 = std::array; + +// 3. 带默认参数的别名模板 +template> +using Heap = std::priority_queue, Compare>; + +int main() { + + Vec numbers = {1, 2, 3}; + Vec3 v3 = {1.0f, 2.0f, 3.0f}; + Heap minHeap; + (void)minHeap; + + d2x_assert_eq(numbers[0], 1); + d2x_assert_eq(numbers[1], 2); + d2x_assert_eq(numbers[2], 3); + + d2x_assert_eq(v3[0], 1.0f); + d2x_assert_eq(v3[1], 2.0f); + d2x_assert_eq(v3[2], 3.0f); + + return 0; +} diff --git a/solutions/cpp11/14-type-alias-3.cpp b/solutions/cpp11/14-type-alias-3.cpp new file mode 100644 index 0000000..dc83708 --- /dev/null +++ b/solutions/cpp11/14-type-alias-3.cpp @@ -0,0 +1,27 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/14-type-alias-3.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/14-type-alias-3.cpp +// + +#include +#include + +// 别名模板必须定义在命名空间作用域(不能写在函数体内) +template +using my_add_pointer_t = typename std::add_pointer::type; + +int main() { + + int c = 20; + my_add_pointer_t ptr = &c; + + bool ok = std::is_same, int*>::value; + + d2x_assert(ok); + d2x_assert_eq(*ptr, 20); + + return 0; +} diff --git a/solutions/cpp11/15-variadic-templates-0.cpp b/solutions/cpp11/15-variadic-templates-0.cpp new file mode 100644 index 0000000..fe8a8c1 --- /dev/null +++ b/solutions/cpp11/15-variadic-templates-0.cpp @@ -0,0 +1,32 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/15-variadic-templates-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/15-variadic-templates-0.cpp +// + +#include +#include + +std::stringstream ss; + +// 定义递归终止函数: 当参数包为空时调用 +void print() {} + +// 定义可变参数模板函数 +template +void print(T first, Args... args) { + ss << first << " "; + // 递归调用,处理剩余参数 + print(args...); +} + +int main() { + print(1, "hello", 3.14); + + std::string result = ss.str(); + d2x_assert(result == "1 hello 3.14 "); + + return 0; +} diff --git a/solutions/cpp11/15-variadic-templates-1.cpp b/solutions/cpp11/15-variadic-templates-1.cpp new file mode 100644 index 0000000..ab75026 --- /dev/null +++ b/solutions/cpp11/15-variadic-templates-1.cpp @@ -0,0 +1,33 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/15-variadic-templates-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/15-variadic-templates-1.cpp +// + +#include + +// 递归终止: 只剩一个参数时直接返回 +template +T sum(T x) { return x; } + +template +T sum(T first, Args... args) { + return first + sum(args...); +} + +int main() { + int res1 = sum(1, 2, 3, 4, 5); + d2x_assert_eq(res1, 15); + + double res2 = sum(1.5, 2.5, 3.0); + d2x_assert(res2 == 7.0); + + // 混合类型 + // 注意: 返回类型由第一个参数 T 决定 + int res3 = sum(10, 20.5); + d2x_assert_eq(res3, 30); + + return 0; +} diff --git a/solutions/cpp11/16-generalized-unions-0.cpp b/solutions/cpp11/16-generalized-unions-0.cpp new file mode 100644 index 0000000..72f36b1 --- /dev/null +++ b/solutions/cpp11/16-generalized-unions-0.cpp @@ -0,0 +1,34 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/16-generalized-unions-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/16-generalized-unions-0.cpp +// + +#include + + +union M +{ + int a1 = 42; // 仅允许一个成员有默认初始化 + int a2; + double a3; + char c; +}; + + +int main() { + + M u1; + //初始化成员 + d2x_assert(u1.a1 == 42); + u1.a2 = 21; + + double val = 3.14; + u1.a3 = val; + + u1.c = 'x'; + + return 0; +} diff --git a/solutions/cpp11/16-generalized-unions-1.cpp b/solutions/cpp11/16-generalized-unions-1.cpp new file mode 100644 index 0000000..7fc63e3 --- /dev/null +++ b/solutions/cpp11/16-generalized-unions-1.cpp @@ -0,0 +1,40 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/16-generalized-unions-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/16-generalized-unions-1.cpp +// + +#include +#include + +union M +{ + int a1; + std::vector a2; + M() {} + M(const std::vector& vec) : a2(vec) { + } + ~M() {} +}; + + +int main() { + + M u1; + u1.a1 = 21; + + // 1. 使用placement new构造 + new (&u1.a2) std::vector(); + + u1.a2 = {1, 42, 3}; + + // 2. 验证:正常访问 + d2x_assert_eq(u1.a2[1], 42); + + // 3. 手动析构 + u1.a2.~vector(); + + return 0; +} diff --git a/solutions/cpp11/17-pod-type-0.cpp b/solutions/cpp11/17-pod-type-0.cpp new file mode 100644 index 0000000..58330f4 --- /dev/null +++ b/solutions/cpp11/17-pod-type-0.cpp @@ -0,0 +1,56 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/17-pod-type-0.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/17-pod-type-0.cpp +// + +#include + +#include + +struct A { + int x; + double y; +}; + +struct B { + A a; + int z; +}; + +struct C { + virtual void foo() {} // 提供定义避免链接错误 + int x; +}; + +struct D { + int x; +private: + int y; +}; + +int main() { + // 1. 判断 A/B 是否为 POD + static_assert(std::is_pod::value && std::is_pod::value, "A and B should be POD"); + + // 2. 判断 C/D 是否不是 POD + static_assert(!std::is_pod::value && !std::is_pod::value, "C and D should not be POD"); + + // 3. 判断 A 是否是 trivial /standard_layout 类型 + bool ok = std::is_trivial::value && std::is_standard_layout::value; + d2x_assert(ok); + + // 4. 判断 B 是否是 trivial /standard_layout 类型 + ok = std::is_trivial::value && std::is_standard_layout::value; + d2x_assert(ok); + + // 5. C 含虚函数 -> 不是 trivial 类型 (assert 期望条件成立, 因此用否定) + ok = !std::is_trivial::value; + d2x_assert(ok); + + // 6. D 同时含 public/private 数据 -> 不是 standard_layout + ok = std::is_standard_layout::value; + d2x_assert(!ok); +} diff --git a/solutions/cpp11/17-pod-type-1.cpp b/solutions/cpp11/17-pod-type-1.cpp new file mode 100644 index 0000000..da5d9e3 --- /dev/null +++ b/solutions/cpp11/17-pod-type-1.cpp @@ -0,0 +1,37 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/17-pod-type-1.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/17-pod-type-1.cpp +// + +#include + +#include +#include + + +struct Packet { + std::uint32_t len; + std::uint16_t type; + std::uint16_t flags; +}; // POD + +int main() { + Packet p1{}; + p1.len = 42; + p1.type = 1; + p1.flags = 0xFF; + + Packet p2{}; + + // 使用 memcpy 在 POD 上做按字节拷贝 + std::memcpy(&p2, &p1, sizeof(Packet)); + + d2x_assert_eq(p2.len, 42u); + d2x_assert_eq(p2.type, static_cast(1)); + d2x_assert_eq(p2.flags, static_cast(0xFF)); + + return 0; +} diff --git a/solutions/cpp11/17-pod-type-2.cpp b/solutions/cpp11/17-pod-type-2.cpp new file mode 100644 index 0000000..457550d --- /dev/null +++ b/solutions/cpp11/17-pod-type-2.cpp @@ -0,0 +1,58 @@ +// d2mcpp: https://github.com/mcpp-community/d2mcpp +// license: Apache-2.0 +// reference solution for: dslings/cpp11/17-pod-type-2.cpp +// +// 用途: 仅给 CI 与维护者参考使用,不是教程入口。 +// 教程练习入口: dslings/cpp11/17-pod-type-2.cpp +// + +#include + +#include +#include + + +// C 风格的消息头部,仅包含 POD 成员,用于跨语言/跨模块传递 +struct MessageHeader { + std::uint32_t len; // payload 长度(字节数) + std::uint16_t type; // 消息类型 + std::uint16_t flags; // 预留标志位 +}; // POD + +// C++ 侧更友好的消息类型,可以使用 std::string 等非 POD 成员 +struct Message { + std::uint16_t type; + std::string payload; +}; + +// 模拟的 C 接口:接收二进制头部数据 +static MessageHeader g_last_header{}; + +extern "C" void c_send_header(const void* data, std::size_t size) { + d2x_assert_eq(size, sizeof(MessageHeader)); + const auto* header = static_cast(data); + g_last_header = *header; +} + +void send_message_to_c(const Message& msg) { + MessageHeader header; + header.len = static_cast(msg.payload.size()); + header.type = msg.type; + header.flags = 0; + c_send_header(&header, sizeof(header)); +} + +int main() { + Message msg{}; + msg.type = 42; + msg.payload = "hello pod"; + + send_message_to_c(msg); + + // 验证 C 接口收到的头部内容是否正确 + d2x_assert_eq(g_last_header.type, msg.type); + d2x_assert_eq(g_last_header.len, static_cast(msg.payload.size())); + d2x_assert_eq(g_last_header.flags, static_cast(0)); + + return 0; +} diff --git a/solutions/cpp11/xmake.lua b/solutions/cpp11/xmake.lua new file mode 100644 index 0000000..5b934eb --- /dev/null +++ b/solutions/cpp11/xmake.lua @@ -0,0 +1,192 @@ +-- Reference solutions for cpp11 chapter exercises. +-- Each target name mirrors dslings/cpp11/ with a "-ref" suffix. +-- Used by CI to verify exercise infrastructure (xmake target / include paths / +-- language standard) and that the canonical answer actually passes all +-- d2x_assert / d2x_assert_eq checks. + +if is_host("windows") then + set_languages("cxx14") +else + set_languages("cxx11") +end + +-- target: cpp11-00-auto-and-decltype + +target("cpp11-00-auto-and-decltype-0-ref") + add_files("00-auto-and-decltype-0.cpp") + +target("cpp11-00-auto-and-decltype-1-ref") + add_files("00-auto-and-decltype-1.cpp") + +target("cpp11-00-auto-and-decltype-2-ref") + add_files("00-auto-and-decltype-2.cpp") + +target("cpp11-00-auto-and-decltype-3-ref") + add_files("00-auto-and-decltype-3.cpp") + +target("cpp11-00-auto-and-decltype-4-ref") + add_files("00-auto-and-decltype-4.cpp") + +-- target: cpp11-01-default-and-delete + +target("cpp11-01-default-and-delete-0-ref") + add_files("01-default-and-delete-0.cpp") + +target("cpp11-01-default-and-delete-1-ref") + add_files("01-default-and-delete-1.cpp") + +target("cpp11-01-default-and-delete-2-ref") + add_files("01-default-and-delete-2.cpp") + +-- target: cpp11-02-final-and-override + +target("cpp11-02-final-and-override-0-ref") + add_files("02-final-and-override-0.cpp") + +target("cpp11-02-final-and-override-1-ref") + add_files("02-final-and-override-1.cpp") + +target("cpp11-02-final-and-override-2-ref") + add_files("02-final-and-override-2.cpp") + +-- target: cpp11-03-trailing-return-type + +target("cpp11-03-trailing-return-type-ref") + add_files("03-trailing-return-type.cpp") + +-- target: cpp11-04-rvalue-references + +target("cpp11-04-rvalue-references-ref") + set_optimize("none") + add_cxxflags("-fno-elide-constructors") + add_files("04-rvalue-references.cpp") + +-- target: cpp11-05-move-semantics + +target("cpp11-05-move-semantics-0-ref") + add_files("05-move-semantics-0.cpp") + +target("cpp11-05-move-semantics-1-ref") + add_files("05-move-semantics-1.cpp") + +target("cpp11-05-move-semantics-2-ref") + add_files("05-move-semantics-2.cpp") + +-- target: cpp11-06-scoped-enums + +target("cpp11-06-scoped-enums-0-ref") + add_files("06-scoped-enums-0.cpp") + +target("cpp11-06-scoped-enums-1-ref") + add_files("06-scoped-enums-1.cpp") + +-- target: cpp11-07-constexpr + +target("cpp11-07-constexpr-0-ref") + add_cxxflags("-Wpedantic -Werror") + add_files("07-constexpr-0.cpp") + +target("cpp11-07-constexpr-1-ref") + add_files("07-constexpr-1.cpp") + +-- target: cpp11-08-literal-type + +target("cpp11-08-literal-type-0-ref") + set_languages("c++17") -- TODO: optimize it + add_files("08-literal-type-0.cpp") + +target("cpp11-08-literal-type-1-ref") + add_files("08-literal-type-1.cpp") + +-- target: cpp11-09-list-initialization + +target("cpp11-09-list-initialization-0-ref") + add_files("09-list-initialization-0.cpp") + +target("cpp11-09-list-initialization-1-ref") + add_files("09-list-initialization-1.cpp") + +target("cpp11-09-list-initialization-2-ref") + add_files("09-list-initialization-2.cpp") + +target("cpp11-09-list-initialization-3-ref") + add_files("09-list-initialization-3.cpp") + +-- target: cpp11-10-delegating-constructors + +target("cpp11-10-delegating-constructors-0-ref") + add_files("10-delegating-constructors-0.cpp") + +target("cpp11-10-delegating-constructors-1-ref") + add_files("10-delegating-constructors-1.cpp") + +-- target: cpp11-11-inherited-constructors + +target("cpp11-11-inherited-constructors-0-ref") + add_files("11-inherited-constructors-0.cpp") + +target("cpp11-11-inherited-constructors-1-ref") + add_files("11-inherited-constructors-1.cpp") + +target("cpp11-11-inherited-constructors-2-ref") + add_files("11-inherited-constructors-2.cpp") + +-- target: cpp11-12-nullptr + +target("cpp11-12-nullptr-0-ref") + add_files("12-nullptr-0.cpp") + +target("cpp11-12-nullptr-1-ref") + add_files("12-nullptr-1.cpp") + +target("cpp11-12-nullptr-2-ref") + add_files("12-nullptr-2.cpp") + +-- target: cpp11-13-long-long + +target("cpp11-13-long-long-0-ref") + add_files("13-long-long-0.cpp") + +target("cpp11-13-long-long-1-ref") + add_files("13-long-long-1.cpp") + +-- target: cpp11-14-type-alias + +target("cpp11-14-type-alias-0-ref") + add_files("14-type-alias-0.cpp") + +target("cpp11-14-type-alias-1-ref") + add_files("14-type-alias-1.cpp") + +target("cpp11-14-type-alias-2-ref") + add_files("14-type-alias-2.cpp") + +target("cpp11-14-type-alias-3-ref") + add_files("14-type-alias-3.cpp") + +-- target: cpp11-15-variadic-templates + +target("cpp11-15-variadic-templates-0-ref") + add_files("15-variadic-templates-0.cpp") + +target("cpp11-15-variadic-templates-1-ref") + add_files("15-variadic-templates-1.cpp") + +-- target: cpp11-16-generalized-unions + +target("cpp11-16-generalized-unions-0-ref") + add_files("16-generalized-unions-0.cpp") + +target("cpp11-16-generalized-unions-1-ref") + add_files("16-generalized-unions-1.cpp") + +-- target: cpp11-17-pod-type + +target("cpp11-17-pod-type-0-ref") + add_files("17-pod-type-0.cpp") + +target("cpp11-17-pod-type-1-ref") + add_files("17-pod-type-1.cpp") + +target("cpp11-17-pod-type-2-ref") + add_files("17-pod-type-2.cpp") diff --git a/solutions/xmake.lua b/solutions/xmake.lua new file mode 100644 index 0000000..f8ed5ab --- /dev/null +++ b/solutions/xmake.lua @@ -0,0 +1,10 @@ +-- Reference solutions root. +-- Mirrors dslings/ structure but only contains the canonical "filled-in" +-- answers, used purely for build / run CI. Each target carries the "-ref" +-- suffix to coexist with the dslings/ targets that share the same logical +-- name (e.g. cpp11-13-long-long-0 vs cpp11-13-long-long-0-ref). +-- +-- Solutions are language-neutral by design: the comments matter only to +-- contributors, not the compiler, so we keep a single zh-style copy. + +includes("cpp11/xmake.lua") diff --git a/xmake.lua b/xmake.lua index d290df8..d54015e 100644 --- a/xmake.lua +++ b/xmake.lua @@ -2,4 +2,5 @@ add_plugindirs("d2x/buildtools") add_includedirs(".") -includes("dslings/xmake.lua") \ No newline at end of file +includes("dslings/xmake.lua") +includes("solutions/xmake.lua") \ No newline at end of file