diff --git a/docs/functions.md b/docs/functions.md index 355ee8f..a45fd1d 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -12,7 +12,7 @@ TODO: println 参数演示 ## 函数的返回值 -函数可以没有返回值,只需要返回类型写 `void` 即可,这样的函数调用的目的只是为了他的副作用(如修改全局变量,输出文本到控制台,修改引用参数等)。 +函数可以没有返回值,只需要声明函数时返回类型声明为 `void` 即可,调用这样的函数只是为了他的副作用(如修改全局变量,输出文本到控制台,修改引用参数等)。 ```cpp void compute() @@ -22,6 +22,7 @@ void compute() ``` > {{ icon.tip }} 对于没有返回值(返回类型为 `void`)的函数,可以省略 `return` 不写。 + ```cpp void compute() { @@ -29,12 +30,54 @@ void compute() } ``` -> {{ icon.warn }} 对于有返回值的函数,必须写 return 语句,如果漏写,会出现可怕的未定义行为 (undefined behaviour)。编译器不会报错,而是到运行时才出现崩溃等现象,建议 GCC 用户开启 `-Werror=return-type` 让编译器检测此类错误。更多未定义行为可以看我们的[未定义行为列表](undef.md)章节。 +> {{ icon.warn }} 对于返回类型不为 `void` 的函数,必须写 `return` 语句,如果漏写,会出现可怕的未定义行为 (undefined behaviour)。编译器不一定会报错,而是到运行时才出现崩溃等现象。建议 GCC 用户开启 `-Werror=return-type` 让编译器在编译时就检测此类错误,MSVC 则是开启 `/we4716`。更多未定义行为可以看我们的[未定义行为列表](undef.md)章节。 +> {{ icon.detail }} 但有两个例外:1. main 函数是特殊的可以不写 return 语句,默认会自动帮你 `return 0;`。2. 具有 co_return 或 co_await 的协程函数可以不写 return 语句。 ### 接住返回值 ### 返回类型 `auto` +C++11 `auto` 可以用作函数的返回类型,但它只是一个**占位**,让我们得以后置返回类型。 + +```cpp +auto f() -> int; +// 等价于: +int f(); +``` + +C++14 引入了函数**返回类型推导**,`auto` 才算真正意义上的用做了函数返回类型,它会根据函数中的 `return` 表达式推导出函数的返回类型。 + +```cpp +int x = 1; +auto f() { + return x; +} +// 等价于: +int f() { + return x; +} + +// 如果函数中没有return语句,那么 `auto` 会被自动推导为 `void` +auto f() { + std::println("hello"); +} +// 等价于: +void f() { + std::println("hello"); +} + +// 值得注意的是,返回类型用 `auto` 来推导的函数,如果有多条 `return` 语句,那么他们必须是相同的类型;否则报错 +auto f(int x) { + if (x > 0) { + return 1; // int + } else { + return 3.14; // double + } +} // 错误:有歧义,无法确定 auto 应该推导为 int 还是 double +``` + + + ## 函数的参数 ### 形参 vs 实参 diff --git a/examples/error_code.cpp b/examples/error_code.cpp index 1fc54d9..7daf8bd 100644 --- a/examples/error_code.cpp +++ b/examples/error_code.cpp @@ -117,8 +117,8 @@ int checkStdError(int ret) { // return {}; // } -template -using expected = tl::expected; +// template +// using expected = tl::expected; struct RAIIFile { int fd; @@ -133,8 +133,60 @@ struct RAIIFile { // } int main() { - RAIIFile file{expectedStdError(open("/tmp/test.log", O_WRONLY)).value()}; + RAIIFile file{expectedStdError(::open("/tmp/test.log", O_WRONLY)).value()}; std::string s = "asasasas"; file.write(s).value(); return 0; } + +namespace screenshot1 { + +namespace std { +using namespace ::tl; +using namespace ::std; +} + +std::expected expectedStdError(int ret) { + if (ret == -1) { + return std::unexpected{std::error_code(errno, std::generic_category())}; + } + return ret; +} + +struct File { + int fd; + + explicit File(const char *path, int flags) { + fd = expectedStdError(::open(path, flags)).value(); + } + + tl::expected write(std::span buf) { + return expectedStdError(::write(fd, buf.data(), buf.size())); + } +}; + +std::expected sqrt(int x) { + if (x < 0) + return std::unexpected{make_error_code(std::errc::argument_out_of_domain)}; + + for (int i = 0;; i++) + if (i * i >= x) + return i; +} + +} + +namespace screenshot2 { + +int sqrt(int x) { + if (x < 0) { + errno = EDOM; + return -1; + } + + for (int i = 0;; i++) + if (i * i >= x) + return i; +} + +}