0%

写在前面

内容见 implicit conversion

限定性转换指的是和 cv 属性有关的类型转换,它的发生必须满足一定的条件。

相似类型

忽略掉各层的 cv 属性,如果两个类型形式相同,就是相似类型。比如 const int* volatile *int** const

Important

函数指针对应函数的参数返回值类型的 cv 属性不可以忽略。可见原页面的例子,这里略去。

限定性分解

参考

C++17 prvalue semantics (“guaranteed copy elision”)

返回值(或函数参数)的位置,如果表达式是纯右值,且返回值(或函数参数)需要的也是同一类型的纯右值,那么标准就要求省略复制和移动。不过,为了让语义检查通过,要构造对象的析构函数必须在此处可以访问,尽管在完成优化之后并不会用到析构函数。

T f()
{
    return U(); // constructs a temporary of type U,
                // then initializes the returned T from the temporary
}
T g()
{
    return T(); // constructs the returned T directly; no move
}
T x = T(T(f())); // x is initialized by the result of f() directly; no move

The C++17 core language specification of prvalues and temporaries is fundamentally different from that of earlier C++ revisions: there is no longer a temporary to copy/move from. Another way to describe C++17 mechanics is “unmaterialized value passing” or “deferred temporary materialization”: prvalues are returned and used without ever materializing a temporary.

非强制的 copy/move elision

编译器一些场景可以对 copy/move 进行省略,但不是强制的(实测 gcc 和 clang 在默认优化等级下就会使用这项优化,而 MSVC 则是在 /O2 才会启用这项优化)。当这项优化发生时,复制 / 移动构造函数必须是可用的,尽管不会被实际执行。

这个问题在 Windows MSYS2 中没有出现,但是在 WSL 上出现了。在调试时对 std::vector 的显示如下:

本来我是用的 CMake 上的调试启动功能的,StackOverflow 上面让用 .vscode/launch.json,我也试了,但是同样不行。这是我写的启动文件内容,主要是在 setupCommands 中打开了 pretty-printing 功能:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gdb - debug active file",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/build/main",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "gdb",
            "setupCommands": [
                {   // Display content in STL containers pretty
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            // "preLaunchTask": "C/C++: g++ build active file"
        }
    ]
}

按照 StackOverflow 的 回答 1,我在 gdb 中用 info pretty-printer 看当前支持的 pretty-printer。结果:

解决方案

假设是 3 号卡,用 sudo lsof /dev/nvidia3 看看有哪些进程正在使用这个设备。然后用各种手段查看各个可疑进程的信息(假设进程号是 66666):

  • ps ww 66666BSD 风格的 ps,而不是 System V)。
  • cat /proc/66666/status 查看进程状态。除了 status 之外,还有其他信息可以在 /proc 下查看。
  • htop -p 66666 或者 top -p 66666

确定进程没用之后,就可以根据 PID 杀掉进程了。

Tip

除了 lsof 之外,还可以用 fuser 查找正打开文件或者 socket 的进程。根据 https://unix.stackexchange.com/a/60497/lsof 更适合找一个进程打开的所有的文件,而 fuser 更适合找特定文件被什么进程打开了,比如 fuser -uv,详见刚刚的链接。

在 Debian 下使用 fuser 需要安装 psmisc(pstree 也是这个包)。

如果不给管理员权限,lsof 可能会输出错误信息并遗漏内容,fuser 则可能会静默失败

现象分析

现象:在 nvidia-smi 上查不到,在 nvitop 上能查到但显示“No Such Process”。

https://en.cppreference.com/w/cpp/language/rule_of_three

Rule of 3

析构函数、拷贝构造、拷贝赋值三者要是定义了其一,最好把其他两个都补上,因为编译生成的很可能不是我们想要的。此外,定义三者之一会导致移动构造、移动赋值被删除

Note

显式删除拷贝构造函数(MyClass(const MyClass&) = delete;)也是一种声明,这样会导致移动构造函数被删除,如果有移动尝试则会匹配上(被删除的)拷贝构造函数,从而编译失败。

Tip

复制 / 移动赋值操作符可以用复制 / 移动构造函数 + std::swap 实现。

Docker 不能用 CTRL + pCTRL + q 退出的解决方案:登录后先用 tty 看自己的终端。然后用另外一个连接 ps -af 看想要终止的 docker 命令的 pid 是多少,然后用 kill -9 强杀。因为杀的是客户端而不是守护进程,所以不需要担心其他正在运行的容器受到影响。

Warning

不能用 kill(不带参数),因为 kill 会让 docker 连接优雅退出,所以容器也会被关闭!

这样能更快找到 docker 命令:

ps -af|grep -iE '(pid|docker)' --color=always|grep -v "grep --color="

查看所有容器的资源占用

docker stats 可以实时刷新所有容器的占用情况。用 docker stats --no-stream 只看最近一次,而不像 top 一样刷新。不知道哪个容器占用多的时候可以使用这个命令。

找到进程属于哪一个容器

我们实验室的不同学生使用不同的容器作为开发环境。现在的问题是,当我们从 htop 或者 top 中发现某个进程消耗了大量资源时,我们需要确定这个进程是由哪个容器启动的,怎么做呢?

pstree -sg 找到进程的父进程,然后在运行的容器中找有没有谁的进程号是能对应的上的。

经过学长提醒,我认识到还可以用 cat /proc/<pid>/cgroup 来查看进程关联信息的方法。如果进程是在容器内运行,输出路径中会包含容器的 id。

https://godbolt.org/z/YrzsWbMh8

在以下几种编译器上都尝试了:

  1. x86-64 gcc 13.2
  2. x86-64 clang 18.1.0
  3. x64 msvc v19.38
  4. x86 msvc v19.38

发现只有 x86 msvc v19.38 给函数名前面加了下划线,其他几种编译器都是尊重函数定义的名字。以前做川合秀实的 30 天自制操作系统时,还以为所有编译器都会固定地给 C 语言函数名前面加上下划线,所以汇编中引用时有一些区别。现在看来只是因为平台不同,所以编译器处理方式也不同。

有两种方案,推荐第 2 种。

方案 1:docker-compose

截至 2024 年 3 月 14 日 最新的 docker-compose 文件是 https://raw.githubusercontent.com/overleaf/overleaf/408e1dccd7c0cb58fe02c5eb9ef983312ad9b244/docker-compose.yml

首先把 docker-compose 配置文件下载下来修改好 sharelatex 的端口、sharelatex/redis/mongo 的映射路径。

❗ 问题:连接不上 redis。这是因为 sharelatex 仍在使用 SHARELATEX 开头的环境变量,所以要把 OVERLEAF 开头的环境变量复制一份改个前缀。改好之后又提示 mongo 连接不上,还是同样的改法。

❗ 问题:登陆网页创建账户,提示请联系管理员。找到 https://github.com/overleaf/overleaf/wiki/Creating-and-managing-users 发现我用的是 legacy 的方法。后面也有解决方案,但是我没有继续往下走了。考虑到过时方法还可能出现别的问题,就换成使用 toolkit 了。

同学把 LaTeX 源码文件夹从 Windows 电脑上拷贝给我,但是无法编译,说是里面有几个字体找不到。我改了 fontspec 的几个设置,包括 \setmainfont 等,向其中用 Path= 选项添加了 fonts/ 的路径(之前没有路径,找的是系统字体)。但是还是不能编译!

然后又慢慢排查,发现是 \maketitle 导致的不能编译,而且也是字体问题,只不过报错的位置变了,一时没看到。点进去看,cls 文件使用了 \arial,而这个字体是这样定义的:

\DeclareRobustCommand{\arial}{\fontspec{Arial}}

虽然 cls 文件设置了文件夹中的 Arial 字体为主字体,但是没有将其声明为一个可以被引用的字体。但是我尝试添加字体的声明也不能成功:

\newfontfamily\arial[Path=extrafonts/]{Arial}