Intellisense and Copy elision
最近开发 co_context
,遇到了很多 C++ 周边的坑。其中一个坑是 intellisense 翻脸不认 C++17 标准的强制拷贝消除(Mandatory elision of copy/move operations)。
这个 intellisense 来自 Vscode Extention: C/C++ (Microsoft)。
Brief Solution
1 |
|
Background
co_context
要开发一个 lock_guard
,但用户必须要写 co_await
,导致只能写成以下形式:
1 | auto lk = co_await mtx.lock_guard(); |
lk
在析构的时候会自动调用 mtx.unlock()
。这看起来很美好,但这里发生了 Copy elision,根据 C++17 标准,这是强制编译器执行的优化,无论拷贝/移动构造函数有没有副作用。为了防止用户误用,我干脆删除了拷贝/移动构造函数,注意这也是符合标准的做法。
但问题是,intellisense 不知道 Copy elision!因为我删除了那些构造函数,导致 intellisense 会报错:「无法引用 xx 构造函数,它已经被删除」。
最糟糕的是,库的用户在正确使用 lock_guard
时,都可能收到这一条错误的报错(哪怕编译通过),这很有迷惑性,显然会大大降低用户体验。
One way: compromise
最初我选择向 intellisense 妥协:你想让我写移动构造,我写就是了。为了语义正确性,我不得不将 lock_guard
类内部的 mutex&
改成 mutex*
,以支持「移动」。
最后在 ~lock_guard()
内,我不得不判断 if (mtx != nullptr) mtx->unlock()
,这很丑,也影响了性能。我不能忍受。
The second way: cheat
错误的根源是 intellisense 不知道 Copy elision。错误的代价不应由我来承担。
我为 lock_guard
增加了移动构造函数,但标注了 [[deprecated]]
,而且内部是 assert(false)
,这是为了防止用户误用。这成功地骗过了 intellisense。
因为保证不会拷贝/移动,所以可以删除 if (mtx != nullptr)
这样的愚蠢代码啦!
The final solution: #ifdef
经过高人指点,欺骗 intellisense 有专门的宏来完成。所以最后的解决办法是:
1 |
|
其中,[[deprecated]]
和 assert
可以删去,因为编译器永远不会处理它们。