Loading... # 1 声明中的()与{}——对象还是函数 ```cpp #include <iostream> struct X { X() { std::cout << "X"; } }; int main() { X x(); } ``` 答案: no output ## 解析 `X x();`被看做函数声明而非`X`对象,构造对象应当使用`X x{};`替代之。 # 2 无限定名字查找顺序 ```cpp #include<iostream> int foo() { return 10; } struct foobar { static int x; static int foo() { return 11; } }; int foobar::x = foo(); int main() { std::cout << foobar::x; } ``` 答案: ```cpp 11 ``` ## 解析 作为类成员(即使是静态成员在类外被调用),优先查找类中的名字。 具体参考[无限定名字查找](https://zh.cppreference.com/w/cpp/language/unqualified_lookup)。 # 3 声明与初始化 ```cpp #include <iostream> int main() { void * p = &p; std::cout << bool(p); } ``` 答案: ```cpp 1 ``` ## 解析 ![image.png](https://zclll.com/usr/uploads/2022/06/1340178890.png) 名字在声明后即刻可用,在其初始化之前。 # 4 延迟查找 ```cpp #include <iostream> using namespace std; template<typename T> void adl(T) { cout << "T"; } struct S { }; template<typename T> void call_adl(T t) { adl(S()); adl(t); } void adl(S) { cout << "S"; } int main () { call_adl(S()); } ``` 答案: ```cpp TS ``` ## 解析 在`call_adt`中,`S()`是非待决名,而`t`是**待决名**,因此对于`adl(t)`的查找,将**延迟到模板实例化点**。 详情见:[待决名](https://zh.cppreference.com/w/cpp/language/dependent_name#.E6.9F.A5.E6.89.BE.E8.A7.84.E5.88.99:~:text=%E5%A6%82%E5%90%8D%E5%AD%97%E6%9F%A5%E6%89%BE%E4%B8%AD%E6%89%80%E8%AE%A8%E8%AE%BA%E7%9A%84%EF%BC%8C%E5%AF%B9%E4%BA%8E%E6%A8%A1%E6%9D%BF%E4%B8%AD%E6%89%80%E4%BD%BF%E7%94%A8%E7%9A%84%E5%BE%85%E5%86%B3%E5%90%8D%E7%9A%84%E6%9F%A5%E6%89%BE%E5%BB%B6%E8%BF%9F%E5%88%B0%E6%A8%A1%E6%9D%BF%E5%AE%9E%E5%8F%82%E5%B7%B2%E7%9F%A5%E6%97%B6%EF%BC%8C%E5%B1%8A%E6%97%B6) # 5 转发引用 ```cpp #include <iostream> #include <utility> int y(int &) { return 1; } int y(int &&) { return 2; } template <class T> int f(T &&x) { return y(x); } template <class T> int g(T &&x) { return y(std::move(x)); } template <class T> int h(T &&x) { return y(std::forward<T>(x)); } int main() { int i = 10; std::cout << f(i) << f(20); std::cout << g(i) << g(20); std::cout << h(i) << h(20); return 0; } ``` 答案: ```cpp 112212 ``` ## 解析 见[我的右值引用文章](https://zclll.com/index.php/cpp/value_category.html)。 # 6 函数类型与函数 ```cpp #include <iostream> template <typename T> void f() { static int stat = 0; std::cout << stat++; } int main() { f<int>(); f<int>(); f<const int>(); } ``` 答案: ```cpp 010 ``` ## 解析 昨天我们说过`f<int>()`和`f<const int>()`的函数类型相同(就是说,用`auto*`指向它们,得到的是同样类型的变量),但是类型相同**并不意味着它们是同一个函数**。事实上,这是两个不同函数。 # 7实参依赖查找 ```cpp #include <iostream> namespace x { class C {}; void f(const C& i) { std::cout << "1"; } } namespace y { void f(const x::C& i) { std::cout << "2"; } } int main() { f(x::C()); } ``` 答案: ```cpp 1 ``` ## 解析 看起来对`f`的调用会找不到相应的函数,但是这里会进行**实参依赖查找**(又称Koenig查找),进入实参所在的`namespace x`,从而在`namespace x`中找到匹配者,之后不会再进行更进一步的查找。 # 8奇怪的括号 ```cpp #include <iostream> struct X { X() { std::cout << "1"; } X(const X &) { std::cout << "3"; } ~X() { std::cout << "2"; } void f() { std::cout << "4"; } } object; int main() { X(object); object.f(); } ``` 答案: ```cpp 11422 ``` ## 解析 这个类似于“Most vexing parse”,实际上`X(object)`不会被当做一个拷贝构造的调用从而产生一个纯右值,**而是会被解析为`X object`。** 这蛮离谱的,但事实就是如此,可以参见: > There is an ambiguity in the grammar involving *[expression-statement](https://timsong-cpp.github.io/cppwp/n4659/stmt.expr#nt:expression-statement)*s and *[declaration](https://timsong-cpp.github.io/cppwp/n4659/dcl.dcl#nt:declaration)*s: An *[expression-statement](https://timsong-cpp.github.io/cppwp/n4659/stmt.expr#nt:expression-statement)* with a [function-style explicit type conversion](https://timsong-cpp.github.io/cppwp/n4659/expr.type.conv) as its leftmost subexpression can be indistinguishable from a *[declaration](https://timsong-cpp.github.io/cppwp/n4659/dcl.dcl#nt:declaration)* where the first *[declarator](https://timsong-cpp.github.io/cppwp/n4659/dcl.decl#nt:declarator)* starts with a **(**. In those cases the *[statement](https://timsong-cpp.github.io/cppwp/n4659/stmt.stmt#nt:statement)* is a *[declaration](https://timsong-cpp.github.io/cppwp/n4659/dcl.dcl#nt:declaration)* . **这再次提醒我们,C++的初始化一定要尽可能的写统一初始化形式`X{object}`啊!** © 允许规范转载 打赏 赞赏作者 赞 如果觉得我的文章对你有用,请随意赞赏
1 条评论
感觉这期两道题都在提醒Most vexing parse这件事。感觉这东西一般书里很少提到啊。统一初始化真的蛮重要。