“我们已经卸载了据称有问题的‘equinusocio.vsc-material-theme’。”
每次我刷新 VScode 时仍然会弹出此通知。
我已经尝试手动从扩展文件夹中删除扩展,但每次我打开 VScode 时都会弹出警报,并显示 VScode 尝试再次安装它。
“我们已经卸载了据称有问题的‘equinusocio.vsc-material-theme’。”
每次我刷新 VScode 时仍然会弹出此通知。
我已经尝试手动从扩展文件夹中删除扩展,但每次我打开 VScode 时都会弹出警报,并显示 VScode 尝试再次安装它。
替换函数(例如names<-
)在像 那样调用时似乎不使用惰性求值names(x) <- c("a", "b")
。
为了演示,让我们定义一个函数来获取数字的小数部分和相应的替换函数 - 但在替换函数内部,包含一行来打印解除约束的value
参数。
fractional <- function(x) {
x %% 1
}
`fractional<-` <- function(x, value) {
print(rlang::enexpr(value))
invisible(x %/% 1 + value)
}
现在如果我们fractional<-
直接调用,它会打印我们给出的表达式value
:
x <- 10.1
`fractional<-`(x, 0.2 + 0.2)
#> 0.2 + 0.2
但是如果我们以赋值形式调用它,它会打印表达式的求值结果:
x <- 10.1
fractional(x) <- 0.2 + 0.2
#> [1] 0.4
语言定义解释了替换函数,例如:
names(x) <- c("a","b")
相当于
`*tmp*` <- x x <- "names<-"(`*tmp*`, value=c("a","b")) rm(`*tmp*`)
但这并不能重现这种行为:
x <- 10.1
`*tmp*` <- x
x <- "fractional<-"(`*tmp*`, value=0.2 + 0.2)
rm(`*tmp*`)
#> 0.2 + 0.2
其中内部发生了什么事情<-
使得它在value
评估后被传递,有什么方法可以规避这种行为?fractional<-
编辑: @SamR 指出使用substitute
捕获了承诺中的表达式:
x <- 10.1
`fractional<-` <- function(x, value) {
print(substitute(value))
invisible(x %/% 1 + value)
}
fractional(x) <- 0.2 + 0.2
#> 0.2 + 0.2
因此,显然我错误地认为value
在传递给之前对进行了评估fractional<-
。但是,我仍然非常想知道为什么 base::substitute
在这里按预期工作,而 rlang::enexpr
和朋友却没有。毕竟,在内部enexpr
使用substitute
:
enexpr <- function(arg) {
.Call(ffi_enexpr, substitute(arg), parent.frame())
}
在 R Studio 中进行调试表明,无论是以赋值形式调用fractional(x) <- 0.2 + 0.2
还是以前缀形式调用时"fractional<-"(x, 0.2 + 0.2)
,fractional<-
都会传递一个未评估的承诺value
:
当以前缀形式调用时,它仍未被计算:
但是在以赋值形式调用时,会在调用之后进行评估enexpr
:
我想知道这是否与在赋值形式中该函数由原始函数调用有关<-
?但不清楚为什么会有所不同。
在 Java 21 和 23 中,java.net.InetAddress
声明为
public sealed class InetAddress implements Serializable permits Inet4Address, Inet6Address {
但是,以下代码:
switch (addr) {
case Inet4Address a -> ...;
case Inet6Address a -> ...;
};
无法编译:
the switch expression does not cover all possible input values
是我遗漏了什么吗,或者这是一个 Java 错误?
该std::align_val_t
类型定义为:
namespace std
{
enum class align_val_t : size_t
{
};
}
这样一个空枚举的用途是什么?
和 有什么区别typedef
?
C++ 标准库中有一个 new std::inplace_vector
,它似乎在编译时具有固定容量。我试图理解std::inplace_vector
而不是std::array
或 的用例std::vector
。它似乎具有固定大小std::inplace_vector
容量的优势,就像 一样,但在插入之前不需要对象初始化。但是,与 不同,它具有固定的编译时容量。std::array
std::vector
std::inplace_vector
std::inplace_vector
有人可以提供一个可能有用的例子吗?
考虑这个空程序:
int main()
{ return 0; }
如果我用 C++ 用 编译它g++ main.cpp && strace ./a.out
,并用 分析输出strace
,我观察到输出的最后几行是(你可以添加-O3
效果是一样的):
mprotect(0x7f71af154000, 45056, PROT_READ) = 0
mprotect(0x7f71af38b000, 4096, PROT_READ) = 0
brk(NULL) = 0xed2000
brk(0xf05000) = 0xf05000
exit_group(0) = ?
+++ exited with 0 +++
但是,如果我这样做:gcc main.cpp && strace ./a.out
mprotect(0x7f4114318000, 16384, PROT_READ) = 0
mprotect(0x7f4114547000, 4096, PROT_READ) = 0
exit_group(0) = ?
+++ exited with 0 +++
如您所见,在 C++ 中有一个额外的brk
函数将堆扩展了 204KB(将两者均转换为十进制后,0xf05000 - 0xed2000 = 204KB)。可以通过将该程序替换为(coliru 链接)轻松验证这一点:
#include <iostream>
#include <unistd.h>
int main()
{
char buf[1024];
sprintf(buf, "pmap -XX %u", getpid());
std::system(buf);
return 0;
}
您可以轻松观察到,使用 进行编译的g++
大小[heap]
为 204KB,并且gcc
行[heap]
甚至从pmap
输出中消失了。
注意:顺便说一下,令我惊讶的是,gcc 对于 include 和in<iostream>
的存在完全没有问题。std
std::system
这 204KB 是用来做什么的?我对这 204KB 一点也不担心,但它引起了我的注意,现在我很好奇。
以下代码生成的程序集在使用-O3
. 为了完整起见,代码始终在 GCC 13.2 中执行 SIMD,而从不在 clang 17.0.1 中执行 SIMD。
#include <array>
__attribute__((noinline)) void fn(std::array<int, 4>& lhs, const std::array<int, 4>& rhs)
{
for (std::size_t idx = 0; idx != 4; ++idx) {
lhs[idx] = lhs[idx] + rhs[idx];
}
}
这是Godbolt 中的链接。
这是 GCC 12.3 的实际汇编(使用 -O3):
fn(std::array<int, 4ul>&, std::array<int, 4ul> const&):
lea rdx, [rsi+4]
mov rax, rdi
sub rax, rdx
cmp rax, 8
jbe .L2
movdqu xmm0, XMMWORD PTR [rsi]
movdqu xmm1, XMMWORD PTR [rdi]
paddd xmm0, xmm1
movups XMMWORD PTR [rdi], xmm0
ret
.L2:
mov eax, DWORD PTR [rsi]
add DWORD PTR [rdi], eax
mov eax, DWORD PTR [rsi+4]
add DWORD PTR [rdi+4], eax
mov eax, DWORD PTR [rsi+8]
add DWORD PTR [rdi+8], eax
mov eax, DWORD PTR [rsi+12]
add DWORD PTR [rdi+12], eax
ret
我非常想知道 a) 前 5 条汇编指令的目的,以及 b) 是否可以采取任何措施使 GCC 12.3 发出 GCC 13.2 的代码(理想情况下,无需手动编写 SSE)。
从文档中可以看出:
pub fn new(x: T) -> Box 在堆上分配内存,然后将 x 放入其中。
但“地点”是一个棘手的词。如果我们写
let arr_boxed = Box::new([0;1000]);
会[0;1000]
在堆上就地初始化吗?
如果我们写
let arr = [0;1000];
let arr_boxed = Box::new(arr);
[0;1000]
编译器是否足够聪明,可以首先在堆上初始化?
这是 python 3.10 中列表理解的反汇编:
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>>
>>> dis.dis("[True for _ in ()]")
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x7fea68e0dc60, file "<dis>", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_CONST 2 (())
8 GET_ITER
10 CALL_FUNCTION 1
12 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7fea68e0dc60, file "<dis>", line 1>:
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 4 (to 14)
6 STORE_FAST 1 (_)
8 LOAD_CONST 0 (True)
10 LIST_APPEND 2
12 JUMP_ABSOLUTE 2 (to 4)
>> 14 RETURN_VALUE
据我了解,它创建了一个名为的代码对象listcomp
,该对象执行实际迭代并返回结果列表,并立即调用它。我无法理解需要创建一个单独的函数来执行这项工作。这是一种优化技巧吗?
我正在尝试移植一个库以在 MSVC 上进行编译。该库将数据存储在向量元组 ( std::tuple<std::vector<Ts>...>
) 中,并使用自定义迭代器同时迭代所有向量(类似于 zip_iterator 的作用)。
迭代器定义的类型如下所示(假设Ts...
-> <int, int>
):
`value_type` is `std::tuple<int, int>`
`reference` is `std::tuple<int&, int&>`
问题是,在最新的 MSVC (v. 19.35) 上,这个迭代器不满足 的概念std::input_iterator
,而在 gcc/clang 上却满足它。
经过进一步调查,我发现失败是由于std::common_reference
元组上的概念行为不一致造成的。
以下static_assert
内容在 MSVC 上失败,而在 gcc/clang 上不会失败
using T = std::tuple<int, int>&;
using U = std::tuple<int&, int&>;
static_assert(std::common_reference_with<T, U>, "failed common_reference_with");
这是Godbolt上的(还有一个迭代器示例)
像这样的类型std::tuple<int, int>&
应该有一个“ common_reference_with
”std::tuple<int&, int&>
吗?MSVC 说不,gcc 说可以。
根据 C++20 及以后的标准,这两种行为中的哪一种是预期的?
是否有任何简单的方法可以使该迭代器成功通过 MSVC 上的迭代器概念检查(即强制这两种类型具有共同的引用)?
我还在 Eric Niebler 的std::common_reference (SO)和代理迭代器(在他的博客上)上找到了一些很好的答案。
但是,我不清楚 C++20 及更高版本中应该发生什么。