Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
L

Loser-HomeWork

> 编程语言
Open source

卢瑟们的作业展示,答案讲解,以及一些C++知识

751 stars0 likes0 views
WebsiteGitHub

About

卢瑟们的作业展示,答案讲解,以及一些C++知识

目录

  • 前言
  • 01 实现管道运算符
    • 运行结果
    • 群友提交
    • 标准答案
    • 解析
  • 02 实现自定义字面量 _f
    • 运行结果
    • 群友提交
    • 标准答案
    • 解析
  • 03 实现 print 以及特化 std::formatter
    • 运行结果
    • 群友提交
    • 标准答案
    • 解析
  • 04 给定类模板修改,让其对每一个不同类型实例化有不同 ID
    • 运行结果
    • 群友提交
    • 标准答案
  • 05 实现 scope_guard 类型
    • 运行结果
    • 群友提交
    • 标准答案
  • 06 解释 std::atomic 初始化
    • 群友提交
    • 标准答案
  • 07 throw new MyException
    • 运行结果
    • 群友提交
    • 标准答案
  • 08 定义array推导指引
    • 运行结果
    • 群友提交
    • 标准答案
  • 09 名字查找的问题
    • 运行结果
    • 群友提交
    • 标准答案
  • 10 遍历任意类数据成员
    • 运行结果
    • 群友提交
    • 标准答案
      • C++17 写法
      • C++20 写法
    • 补充说明
  • 11 emplace_back() 的问题
    • 群友提交
    • 标准答案
  • 12 实现make_vector()
    • 运行结果
    • 群友提交
    • 标准答案
      • 运行结果
  • 13 关于 return std::move
    • 群友提交
    • 标准答案
  • 14 以特殊方法修改命名空间中声明的对象
    • 运行结果
    • 群友提交
    • 标准答案
      • 利用符号来解决
      • 直接修改内存
      • 利用名字查找规则
  • 15 表达式模板
    • 群友提交
    • 标准答案
    • 解析
  • 16 制造传递函数模板的宏
    • 群友提交
    • 标准答案

前言

卢瑟们的作业展示。

提交 PR 不应当更改当前 README,请将作业提交到 src\群友提交 中,比如你要提交第一个作业:

你应当在 src\群友提交\第01题 中创建一个自己的 .md 或 .cpp 文件,文件名以自己交流群 ID 命名(或 GitHub 用户名都可,方便找到本人即可)。

答题的一般要求如下(题目额外要求也自行注意看):

  1. 不更改 main 函数,不得使其不运行(意思别捞偏门)。
  2. 自行添加代码,在满足第一点的要求下,要能成功编译运行并与 给出运行结果一致。

01 实现管道运算符

日期:2023/7/21 出题人:mq白

给出代码:

int main(){
    std::vector v{1, 2, 3};
    std::function f {[](const int& i) {std::cout 
    群友提交

### [群友提交](src/群友提交/第01题)

答题者:[**`andyli`**](src/群友提交/第01题/andyli.cpp)

```cpp
#include 
#include 
#include 
#include 

template 
auto operator|(R&& r, F&& f) {
    for (auto&& x: r)
        f(x);
    return r;
}
int main() {
    std::vector v{1, 2, 3};
    std::function f{[](const int& i) { std::cout  很常规,没啥问题。

答题者:[**`mq松鼠`**](src/群友提交/第01题/mq松鼠.cpp)

```cpp
#include 
#include 
#include 

auto operator | (std::vector&& v,std::function f){
    for(auto&i:v){
        f(i);
    }
    return v;
}
auto operator | (std::vector& v,std::function f){
    for(auto&i:v){
        f(i);
    }
    return v;
}
int main(){
    std::vector v{1, 2, 3};
    std::function f {[](const int& i) {std::cout  评价:闲的没事多写个重载,裱起来。

    标准答案

### 标准答案

```cpp
template
    requires std::regular_invocable//可加可不加,不会就不加
std::vector& operator|(std::vector& v1, F f) {
    for (auto& i : v1) {
        f(i);
    }
    return v1;
}

不使用模板:

std::vector& operator|(std::vector& v1, const std::function& f) {
    for (auto& i : v1) {
        f(i);
    }
    return v1;
}

不使用范围 for,使用 C++20 简写函数模板:

std::vector& operator|(auto& v1, const auto& f) {
    std::ranges::for_each(v1, f);
    return v1;
}

各种其他答案的范式无非就是这些改来改去了,没必要再写。

解析

解析

很明显我们需要重载管道运算符 |,根据我们的调用形式 v | f2 | f, 这种链式的调用,以及根据给出运行结果,我们可以知道,重载函数应当返回 v 的引用,并且 v 会被修改。 v | f2 调用 operator |,operator | 中使用 f2 遍历了 v 中的每一个元素,然后返回 v 的引用,再 | f。

template
requires std::regular_invocable //我们可以认为对模板形参U,F满足std::regular_invocable的约束

如果没接触过约束表达式,没关系,下面将简要的介绍。

requires 表达式如同一个返回 bool 的函数,而 U 和 F 作为类型填入 std::regular_invocable 的实参列表里,只要作为类型的 U、F 满足该表达式则返回 true;不满足则返回 false,称为“不满足约束”。不满足约束的类型自然不会执行后续的代码。

而 std::regular_invocable 我们可以简单看成对类型 U 的每一个值,我们是否可以调用函数 F,即调用 std::invoke 。

这就相当于我们在编译期对运行期做了想象,想象是否可以对 U 在运行期执行 F。如果可以那满足约束。

而函数主体则极为简单

std::vector& operator|(std::vector& v1, const F f) {
    for (auto& i : v1) {
        f(i);
    }
    return v1;
}

其中范围表达式 for (auto& i : v1),如同 for(auto i=v.begin();i!=v.end();++i){f(*i)}:我们对 vector(范围)中的每一个元素应用一次 f 函数。返回时照常返回 v1。

如若不使用模板,则我们的形参列表得用 std::function 来接住我们使用的函数:

对范围中的每个成员应用 f 不需要返回值且需要对范围中的元素进行修改,所以第二个形参为 std::function。并且我们不需要对传进来的函数 f 进行修改与拷贝,所以加上 const 限定是个好习惯。

同样的我们可以不使用范围 for 而是更简单的 std::ranges::for_each(v1, f); 即同上一样对范围 v1内的每个元素,应用一次函数 f。

对于使用模板的形式,我们可以使用 C++20 的简写函数模板;简而言之,在函数形参列表中 auto 占位符会为模板形参列表追加一个虚设的模板形参。最开始的模板形式可以写成

std::vector& operator|(auto& v1, const auto& f) 

它和原形式相同。


02 实现自定义字面量 _f

日期:2023/7/22 出题人:mq白

给出代码:

int main(){
    std::cout > n;
    std::cout , n);
}

运行结果

乐 :5 *
乐 :5 5 *
乐 :1010101 *
卢瑟******
6
π:3.141593
  • 难度:★★☆☆☆

6 为输入,决定 $\pi$ 的小数点后的位数,可自行输入更大或更小数字。 提示:C++11 用户定义字面量、C++20 format 库。 难度:★★☆☆☆

群友提交

群友提交

答题者:andyli

#include 
#include 
#include 
#include 

namespace impl {
    struct Helper {
        const std::string_view s;
        Helper(const char* s, std::size_t len): s(s, len) {}
        template 
        std::string operator()(Args&&... args) const {
            return std::vformat(s, std::make_format_args(args...));
        }
    };
} // namespace impl
impl::Helper operator""_f(const char* s, std::size_t len) noexcept {
    return {s, len};
}

int main() {
    std::cout > n;
    std::cout , n);
}
标准答案

标准答案

constexpr auto operator""_f(const char* fmt, size_t) {
    return[=](T&&... Args) { return std::vformat(fmt, std::make_format_args(Args...)); };
}
解析

解析

我们需要使用到 C++11 用户定义字面量,""_f 正是用户自定义字面量。

但字面量运算符(用户定义字面量所调用的函数被称为字面量运算符)的形参列表有一些限制,我们需要的是 const char *, std::size_t 这样的形参列表,恰好这是允许的;而字面量运算符的返回类型需要自定义,这个类型需要在内部重载 operator(),以满足上述字面量像函数一样调用的要求。

我们一步一步来:

void operator""_test(const char* str, std::size_t){
    std::cout 
        std::string operator()(Args&&... args) const {
            return std::vformat(s, std::make_format_args(args...));
        }
    };
} // namespace impl
impl::Helper operator""_f(const char* s, std::size_t len) noexcept {
    return {s, len};
}

operator""_f 本身非常简单,只是用来把传入的参数(格式字符串)和长度,构造 impl::Helper 对象再返回。Helper 类型使用了一个 string_view 作为数据成员,存储了格式字符串,以供后面格式化使用。

重点只在于 operator()。 它是一个变参模板,用来接取我们传入的任意类型和个数的参数,然后返回格式化后的字符串。

这里用到的是 std::vformat 进行格式化,它的第一个参数是格式字符串,也就是我们要按照什么样的规则去格式化;第二个参数是要格式化的参数,但是我们没有办法直接进行形参包展开,它第二个参数的类型实际上是 std::format_args。 我们必须使用 std::make_format_args 函数传入我们的参数,它会返回 std::format_args 类型,其实也就是相当于转换一下,合理。

不过显然标准答案不是这样的,还能简化,直接让 ""_f 返回一个 lambda 表达式即可。


03 实现 print 以及特化 std::formatter

日期:2023/7/24 出题人:mq白

实现一个print,如果你做了上一个作业,我相信这很简单。 要求调用形式为:

print(格式字符串,任意类型和个数的符合格式字符串要求的参数)
struct Frac {
   int a, b;
};

给出自定义类型Frace,要求支持

Frac f{ 1,10 };
print("{}", f);// 结果为1/10

运行结果

1/10
  • 难度:★★★☆☆

禁止面向结果编程,使用宏等等方式,最多B(指评价),本作业主要考察和学习format库罢了。

提示: std::formatter

提交代码最好是网上编译了三个平台的截图,如:

群友提交

群友提交

标准答案

标准答案

template<>
struct std::formatter:std::formatter{
    auto format(const auto& frac, auto& ctx)const{//const修饰是必须的
        return std::format_to(ctx.out(), "{}/{}", frac.a, frac.b);
    }
};
void print(std::string_view fmt,auto&&...args){
    std::cout 

    解析

### 解析

实现一个 print 很简单,我们只要按第二题的思路来就行了,一个格式化字符串,用 std::string_view 做第一个形参,另外需要任意参数和个数,使用形参包即可。

```cpp
void print(std::string_view fmt,auto&&...args){
    std::cout ` 的 **parse** 函数,独立实现 **format** 函数。如果不了解此处模板特化的语法,请复习[模板特化](https://zh.cppreference.com/w/cpp/language/template_specialization)。

```cpp
template<>
struct std::formatter : std::formatter {
    auto format(const auto& frac, auto& ctx)const{//const修饰是必须的
        return std::format_to(ctx.out(), "{}/{}", frac.a, frac.b);
    }
};

我们同样使用 auto 作占位符的简写函数模板,对于 format 函数,首个参数为我们传递的自定义类,第二个参数(ctx)为我们要传递给 std::format_to 输出迭代器的格式字符串。

在函数体中我们直接返回 std::format_to() 调用表达式的结果,此函数返回输出迭代器;返回值我们使用 auto 占位符进行返回值推导。

在函数实参中,ctx.out() 即为输出迭代器,第二个参数为可转换为 std::string_view 或 std::wstring_view,而转换结果是常量表达式和 Args 的合法格式字符串。本题中我们填入我们需要的形式,即 {}/{}。

我们想要两个参数塞进 {},就如我们使用 printf(%d,x) 一样;最后两个参数为“需要塞进 {} 的值”,即要格式化的参数。


04 给定类模板修改,让其对每一个不同类型实例化有不同 ID

日期:2023/7/25 出题人:Adttil

#include
class ComponentBase{
protected:
    static inline std::size_t component_type_count = 0;
};
template
class Component : public ComponentBase{
public:
    //todo...
    //使用任意方式更改当前模板类,使得对于任意类型X,若其继承自Component

    //则X::component_type_id()会得到一个独一无二的size_t类型的id(对于不同的X类型返回的值应不同)
    //要求:不能使用std::type_info(禁用typeid关键字),所有id从0开始连续。
};
class A : public Component
{};
class B : public Component
{};
class C : public Component
{};
int main()
{
    std::cout 
    群友提交

### [群友提交](src/群友提交/第04题)

    标准答案

### 标准答案

```cpp
template
class Component : public ComponentBase{
public:
    static std::size_t component_type_id(){
        static std::size_t ID = component_type_count++;
        return ID;
    }
};

分析:

我们需要实现 Component 的静态成员函数 component_type_id。这是从给出代码得知的:

class A : public Component
{};
A::component_type_id()

题目要求是每一个自定义类类型(假设是 X)继承 Component,调用 component_type_id() 返回的是自己独一无二的 ID。其他的类型同理。

解决题目之前我们需要强调一个知识点:

C++ 的模板不是具体类型,实例化之后才是(即函数模板不是函数,类模板不是类),类模板的静态成员或静态成员函数也不属于模板,而是属于实例化后的具体类型,我们可以用一段代码来展示结论:

#include 

template
struct Test{
    inline static int n = 10;
};

int main(){
    Test::n = 1;
    std::cout ::n ::n` 和 `Test::n` 不是相同的 n,并且 `Test` 和 `Test` 也不是一种类型(静态成员函数同理)。

所以我们的解法利用的是:不同的类型实例化 `Component` 类模板,也是不同的静态成员函数,静态成员函数里面的静态局部也都是唯一的,并且在第一次调用的时候才会初始化,后面就不会。

---

## `05` 实现 `scope_guard` 类型

日期:**`2023/7/29`** 出题人:[Da'Inihlus](https://github.com/dynilath)

要求实现 **`scope_guard`** 类型 ( 即支持传入任意可调用类型 , 析构的时候同时调用 )。

```cpp
#include 
#include 

#include 
#include 
#include 

struct X {
    X() { puts("X()"); }
    X(const X&) { puts("X(const X&)"); }
    X(X&&) noexcept { puts("X(X&&)"); }
    ~X() { puts("~X()"); }
};

int main() {
    {
        // scope_guard的作用之一,是让各种C风格指针接口作为局部变量时也能得到RAII支持
        // 这也是本题的基础要求
        FILE * fp = nullptr;
        try{
            fp = fopen("test.txt","a");
            auto guard = scope_guard([&] {
                fclose(fp);
                fp = nullptr;
            });

            throw std::runtime_error{"Test"};
        } catch(std::exception & e){
            puts(e.what());
        }
        assert(fp == nullptr);
    }
    puts("----------");
    {
        // 附加要求1,支持函数对象调用
        struct Test {
            void operator()(X* x) {
                delete x;
            }
        } t;
        auto x = new X{};
        auto guard = scope_guard(t, x);
    }
    puts("----------");
    {
        // 附加要求2,支持成员函数和std::ref
        auto x = new X{};
        {
            struct Test {
                void f(X*& px) {
                    dele

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C++cppcpp20homeworkloser

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言