Loading...

1 C语言链接

#include<iostream> namespace A{ extern "C" int x; }; namespace B{ extern "C" int x; }; int A::x = 0; int main(){ std::cout << B::x; A::x=1; std::cout << B::x; }

答案:

01

解析

C语言中没有namespace,因此指定了C链接后,A::xB::x指代同一个对象。

2 模板显式偏特化

#include <iostream> template<typename T> T sum(T arg) { return arg; } template<typename T, typename ...Args> T sum(T arg, Args... args) { return arg + sum<T>(args...); } int main() { auto n1 = sum(0.5, 1, 0.5, 1); auto n2 = sum(1, 0.5, 1, 0.5); std::cout << n1 << n2; }

答案:

32

解析

第一个递归中T一直被指定为double,第二个一直被指定为int。

3 函数类型推导中的cv限定&螺旋修饰规则

#include <iostream> #include <type_traits> int main() { std::cout << std::is_same_v< void(int), void(const int)>; std::cout << std::is_same_v< void(int*), void(const int*)>; }

答案:

01

解析

首先是函数类型推导中,参数直接cv-限定会被去除:

The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. (...) After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.

image.png

image.png

其次是const螺旋修饰规则告诉我们const int*int const*都是指向const int的指针(指向intconst指针是int* const),因此const int*不会去除const,所以是不相同的类型。

4 auto作为返回值推导

#include <iostream> auto sum(int i) { if (i == 1) return i; else return sum(i-1)+i; } int main() { std::cout << sum(2); }

答案:

3

解析

auto作为返回值推导时,可以存在多条return语句,只要推导结果相同即可。而在return语句推导时,只要推导出一处具体的结果,即可用于与别处匹配,因此第二条return中的sum(i-1)在此处直接作为int,检查无矛盾即可。

5 类类型的默认访问控制

#include <iostream> class A {}; class B { public: int x = 0; }; class C : public A, B {}; struct D : private A, B {}; int main() { C c; c.x = 3; D d; d.x = 3; std::cout << c.x << d.x; }

答案:

CE

解析

C++中structclass一样具有访问控制,class中默认成员访问为private,默认继承为privatestruct中默认成员访问为public,默认继承为public。本题中做了显式声明,从声明。

6 虚函数默认实参

#include <iostream> struct A { virtual void foo (int a = 1) { std::cout << "A" << a; } }; struct B : A { virtual void foo (int a = 2) { std::cout << "B" << a; } }; int main () { A *b = new B; b->foo(); }

答案:

B1

解析

规则:

A virtual function call (§ 13.3) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides.

具体实践来说,默认实参是在编译期在调用处直接写好的,这很好理解。动态绑定遵循了这一点。

7 表达式求值顺序

#include <iostream> int main() { int I = 1, J = 1, K = 1; std::cout << (++I || ++J && ++K); std::cout << I << J << K; }

答案:

1211

解析

6) 内建逻辑与 (AND) 运算符 && 和内建逻辑或 (OR) 运算符 || 的第一(左)操作数的每个值计算和副作用,按顺序早于第二(右)操作数的每个值计算和副作用。

  1. 每个使用内建(非重载)运算符的下列四种表达式的求值中,表达式 a 的求值后有一个序列点。a && b
    a || b
    a ? b : c
    a , b

8 字符串常量

#include <iostream> int main() { char* a = const_cast<char*>("Hello"); a[4] = '\0'; std::cout << a; }

答案:

UB

解析

字符串常量"Hello"位于常量区,修改是UB。

9 字符串字面量

#include <iostream> int main() { std::cout << sizeof(""); }

答案:

1

解析

字符串字面量被解释为char[]而非char*,那么自动添加'\0'后大小为1。

10 重载决议

#include<iostream> template<typename T> void foo(T...) {std::cout << 'A';} template<typename... T> void foo(T...) {std::cout << 'B';} int main(){ foo(1); foo(1,2); }

答案:

AB

解析

这个知识点实在是太过复杂了,可以看cppreference上的介绍。当然,更复杂的情况,我们可能要先考虑偏特化决议,再去进行重载决议。

总的来说,这两种决议都遵循一个原则:最佳匹配如果存在,那么它不能在任何一个参数上比其他重载更不特殊,且至少在一个参数上更加特殊。

至于具体规则……这个世界上真的有人能完全掌握吗?……

如果觉得我的文章对你有用,请随意赞赏