摘自K&R第99页:
“我们更喜欢后者,因为它更明确地表明该变量是一个指针。”
我需要澄清一下为什么“更明确地说明变量是指针”会有什么重要性?我预计原因是为了让程序更快而不是更明确,如第 97 页所述:
“指针版本通常会更快,但至少对于初学者来说,会更难理解”
但在这种情况下,为什么指针版本会更快? 如果arr[i]
只是相当于为什么*(a+i)
程序会更快? 是因为 C 不需要将数组转换为指针版本吗?
在定义泛型 fallback 时,我遇到了传统SFINAE(使用type_traits
和std::void_t
)与现代C++20 概念operator<<
之间的意外行为差异。目的很简单:创建一个泛型,仅当通过参数相关查找 (ADL)operator<<
未找到任何现有自定义定义时才启用。operator<<
使用特征( )的传统基于SFINAEis_std_streamable
的检测工作符合预期,其定义为:
template <class T, class = void>
struct is_std_streamable : std::false_type {};
template <class T>
struct is_std_streamable<T, std::void_t<decltype(std::declval<std::ostream&>() << std::declval<const T&>())>> : std::true_type {};
基于概念的检测(StdStreamable
)定义为:
template <class T>
concept StdStreamable = requires(const T t, std::ostream& os) {
{ os << t } -> std::same_as<std::ostream&>;
};
通用的后备operator<<
看起来像这样(requires
注释掉的子句):
template <StdPrintable T>
// requires(!StdStreamable<T>)
// requires(!is_std_streamable<T>::value)
std::enable_if_t<!is_std_streamable<T>::value, std::ostream&>
operator<<(std::ostream& os, T const& val) {
...
}
取消注释基于概念requires
的子句(requires(!StdStreamable<T>)
或)时requires(!is_std_streamable<T>::value)
,GCC和Clang都会产生以下循环约束错误:
error: satisfaction of constraint 'StdStreamable<T>' depends on itself
我理解,在定义新版本的子句std::declval<std::ostream&>() << std::declval<const T&>()
中使用表达式可能会被编译器解释为循环依赖。但为什么C++20 概念会触发此循环约束问题,而传统的SFINAE不会?这种行为是标准规定的,还是概念的已知限制,或者可能是编译器错误?requires
operator<<
完整、最小、可重现的示例和其他详细信息:
提前致谢。
在 C++ 中,可以创建对“未知边界数组”的引用。
例子:const char (&)[]
在 C++20 之前,clang(14.x、18.x)不允许您将对已知边界的数组的引用强制转换为对未知边界的数组的引用,并且会失败并出现错误:
对不完整类型“const char[]”的引用无法绑定到类型“const char[5]”的左值
对于 C++20,clang 现在允许这样做。
另一方面,gcc 一直允许这样做,一直追溯到 C++98。
https://godbolt.org/z/66sh8a5z1
int main() {
using StringLitRef = const char (&)[];
StringLitRef array = "derp"; // works on g++ for any standard and clang with c++20
return 0;
}
“我们已经卸载了据称有问题的‘equinusocio.vsc-material-theme’。”
每次我刷新 VScode 时仍然会弹出此通知。
我已经尝试手动从扩展文件夹中删除扩展,但每次我打开 VScode 时都会弹出警报,并显示 VScode 尝试再次安装它。
集合是无序的,或者说它们的顺序是一个实现细节。我对这个细节很感兴趣。我看到了一个让我惊讶的案例:
print({2, 3, 10})
x = 2
print({x, 3, 10})
输出(在线尝试!):
{3, 10, 2}
{10, 2, 3}
尽管相同的元素以相同的顺序写入,但它们的排序却不同。这是怎么发生的?这是故意为之吗?例如,为了优化查找速度?
我的sys.version
和sys.implementation
:
3.13.0 (main, Nov 9 2024, 10:04:25) [GCC 14.2.1 20240910]
namespace(name='cpython', cache_tag='cpython-313', version=sys.version_info(major=3, minor=13, micro=0, releaselevel='final', serial=0), hexversion=51183856, _multiarch='x86_64-linux-gnu')
替换函数(例如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 错误?
我没有将任何东西编译为本机,换句话说,我没有使用native-image
GraalVM。我只是使用 GraalVM 运行相同的 Java 类(相同的 Java 字节码),然后使用常规 Oracle JVM 运行相同的 Java 类(相同的 Java 字节码)。
我使用哪个版本的 Java 或平台并不重要(我在 Linux 和 Mac 上进行了测试)。GraalVM 总是比任何其他常规 JVM 快得多(30 倍)。
看起来常规 JVM 未使用其 JIT 正确优化该方法。请注意,该方法非常简单且很小。
有谁知道为什么会出现这种情况,以及我如何在常规 JVM 中修复这个问题?目前唯一的解决方法是迁移到 GraalVM。编译并运行下面的代码来重现该问题非常容易。只需先使用 Oracle JVM 编译然后运行,然后使用任何 Graal JVM 即可看到差异。
谢谢!
public class OracleJvm23MathBug {
// simple and small amount of math
// =====> should be optimized/compiled/inlined for sure!
private static final long doSomething(int load, int i) {
long x = 0;
for (int j = 0; j < load; j++) {
long pow = (i % 8) * (i % 16);
if (i % 2 == 0) {
x += pow;
} else {
x -= pow;
}
}
return x;
}
/*
* Execute this with OpenJDK/Zulu/Oracle JVM 23 => average 215 nanoseconds
* Now execute this with Graal23 JVM 23 => average 7 nanoseconds
*
* This bug can be observed in any platform (I tested on Linux and Mac)
*
* $ java -version
* java version "23.0.1" 2024-10-15
* Java(TM) SE Runtime Environment (build 23.0.1+11-39)
* Java HotSpot(TM) 64-Bit Server VM (build 23.0.1+11-39, mixed mode, sharing)
*
* $ java -cp . OracleJvm23MathBug
* Value computed: -550000000000
* Measurements: 10000000| Avg Time: 215 nanos | Min Time: 83 nanos | Max Time: 199750 nanos
*
* $ java -version
* java version "23.0.1" 2024-10-15
* Java(TM) SE Runtime Environment Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01)
* Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 23.0.1+11.1 (build 23.0.1+11-jvmci-b01, mixed mode, sharing)
*
* $ java -cp . OracleJvm23MathBug
* Value computed: -550000000000
* Measurements: 10000000| Avg Time: 7 nanos | Min Time: 0 nanos | Max Time: 178625 nanos
*/
public static final void main(String[] args) {
final int iterations = 10_000_000;
final int load = 10_000;
NanoBench bench = new NanoBench();
long computed = 0;
for (int i = 0; i < iterations; i++) {
bench.mark();
computed += doSomething(load, i);
bench.measure();
}
System.out.println("Value computed: " + computed);
bench.printResults();
}
private static class NanoBench {
private int measurements;
private long totalTime, minTime, maxTime, time;
private final StringBuilder sb = new StringBuilder(128);
NanoBench() {
reset();
}
public final void reset() {
totalTime = time = measurements = 0;
maxTime = Long.MIN_VALUE;
minTime = Long.MAX_VALUE;
}
public final void mark() {
time = System.nanoTime();
}
public final void measure() {
long lastNanoTime = System.nanoTime() - time;
totalTime += lastNanoTime;
minTime = lastNanoTime < minTime ? lastNanoTime : minTime;
maxTime = lastNanoTime > maxTime ? lastNanoTime : maxTime;
measurements++;
}
public final void printResults() {
sb.setLength(0);
sb.append("Measurements: ").append(measurements);
sb.append("| Avg Time: ").append((long) (totalTime / (double) measurements)).append(" nanos");
sb.append(" | Min Time: ").append(minTime).append(" nanos");
sb.append(" | Max Time: ").append(maxTime).append(" nanos\n\n");
for (int i = 0; i < sb.length(); i++) System.out.print(sb.charAt(i));
}
}
}
该std::align_val_t
类型定义为:
namespace std
{
enum class align_val_t : size_t
{
};
}
这样一个空枚举的用途是什么?
和 有什么区别typedef
?
前段时间,我偶然发现了 C 语言构造(例如(expr0, expr1, expr2)
)如何求值的想法(有关更多上下文,请参阅“逗号运算符 , 的作用是什么?” )。
我已经开始尝试这个,特别是在类似函数的宏中,最近发现了一个代码,它被一些编译器拒绝,而其他编译器却可以接受。它看起来类似于以下代码片段:
#include <stdio.h>
int main(void)
{
int arr[] = {0};
(1, arr[0]) = 30; // <--- potentially (in)valid code
printf("%d\n", arr[0]);
return 0;
}
如您所见,为了使其工作,(1, arr[0])
必须将其求值为左值arr[0]
,否则将无法赋值。但是,我不确定上述行为是否有效。它“有意义”,而且我发现了它的用途,但我也明白为什么编译器开发人员会拒绝它。
上述代码被 gcc、clang 和 msvc 拒绝(请注意,msvc 主要是一个 C++ 编译器,而 gcc 和 clang 是 C 前端):
$ gcc main.c
main.c: In function ‘main’:
main.c:6:21: error: lvalue required as left operand of assignment
6 | (1, arr[0]) = 30;
| ^
$ clang main.c -Wno-unused-value
main.c:6:14: error: expression is not assignable
6 | (1, arr[0]) = 30;
| ~~~~~~~~~~~ ^
1 error generated.
$ cl main.c /nologo
main.c
main.c(6): error C2106: '=': left operand must be l-value
作为比较,g++、clang++ 和 tcc 都可以运行上述代码(注意,tcc 是 C 编译器,而 g++ 和 clang++ 是 C++ 前端):
$ tcc main.c && ./a.out
30
$ g++ main.c && ./a.out
30
$ clang++ main.c -Wno-unused-value -Wno-deprecated && ./out
30
我还尝试了一些不同的命令选项,例如明确将 msvc 设置为在/std:c++latest
和/std:c99
模式下运行,或者为 gcc/clang/g++/clang++ 设置不同的选项-std
,但它没有改变任何东西。
起初,我以为这是 tcc 中的一个错误,因为它是唯一一款不会拒绝“错误”代码的 C 编译器,但后来我检查了 C++ 前端,我不再那么确定了。尤其是因为 msvc 会拒绝它,而 g++/clang++ 则不会。
作为参考,我在 x86_64 Linux 上,使用 gcc/g++ 14.2.1、clang 18.1.8、msvc 19.40.33811(通过 wine 运行)和 tcc 0.9.28rc(mob@08a4c52d)。
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 一点也不担心,但它引起了我的注意,现在我很好奇。
以下 bash 脚本:
#!/bin/bash
TEST_VAR=""
if [[ -z $TEST_VAR ]] then
echo "empty"
else
echo "$TEST_VAR"
fi
尽管“then”之前缺少一个分号,但在 bash 5.2 中不会引发错误并成功完成,甚至返回0
。
在运行 bash 5.1 或 5.0 的系统上运行上述脚本会按预期失败。
这是为什么?bash 在 5.2 版中更改了解析器吗?搜索后,我找不到任何关于此更改的参考资料。bash 是否故意在此处更改了行为,或者这是一个错误?
作为计算机安全入门课程的一部分,我们有一个关于 SQL 注入的简短单元。其中一项作业是一个基本的未经清理的登录页面。预期的解决方案是沿着经典路线的' or 1=1; --
,但我们始终欢迎学生找到非传统的解决方案。
最近向我提出了这样的解决方案:输入'--'
密码似乎执行了成功的 SQL 注入。这将导致查询被评估为如下所示:
SELECT * FROM users WHERE name='admin' AND password=''--'';
这里,--'';
不被解析为注释,因为 MariaDB 要求注释后面跟空格。事实上,如果双破折号被解析为注释,则该查询根本不会返回任何内容;password=''
同样地,假设密码非空,我们将得到,其计算结果为 false。
末尾的额外一对引号似乎是必要的:将其保留为password=''--;
或在其后面插入其他数据 ( password=''--1;
) 会导致条件被评估为 false,如预期的那样。
一些快速测试无法在其他数据库中重现这种行为——据我所知,这是 MariaDB 特有的行为。该文档确认两个不带空格的破折号不会被解析为注释,但没有详细说明它们被解析为什么。编辑:不知何故,我设法忽略了 MySQL 中也发生这种情况的事实。事实上,这种行为发生在任何 MySQL 分支(不仅仅是 MariaDB)中。
--
当它后面没有空格时会发生什么?为什么它会导致比较结果为 true?
一个玩具示例:
CREATE TABLE users (
userid INTEGER PRIMARY KEY,
username TEXT NOT NULL,
password TEXT NOT NULL
);
INSERT INTO users VALUES (0001, 'admin', 'S3cur3P4ssw0rd!');
INSERT INTO users VALUES (0002, 'generic_user', 'Password');
SELECT * FROM users WHERE username='admin' AND password=''; -- empty password, query returns no users
SELECT * FROM users WHERE username='admin' AND password=''-- ''; -- parsed as comment, equivalent to above query, returns no users
SELECT * FROM users WHERE username='admin' AND password=''--''; -- query returns admin user
SELECT * FROM users WHERE username='admin' AND password=''--; -- query returns zero users
SELECT * FROM users WHERE username='admin' AND password=''--1; -- query returns zero users
通常,如果您尝试为同一关键字参数传递多个值,您会收到 TypeError:
In [1]: dict(id=1, **{'id': 2})
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [1], in <cell line: 1>()
----> 1 dict(id=1, **{'id': 2})
TypeError: dict() got multiple values for keyword argument 'id'
但是,如果您在处理另一个异常时执行此操作,则会收到 KeyError:
In [2]: try:
...: raise ValueError('foo') # no matter what kind of exception
...: except:
...: dict(id=1, **{'id': 2}) # raises: KeyError: 'id'
...:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [2], in <cell line: 1>()
1 try:
----> 2 raise ValueError('foo') # no matter what kind of exception
3 except:
ValueError: foo
During handling of the above exception, another exception occurred:
KeyError Traceback (most recent call last)
Input In [2], in <cell line: 1>()
2 raise ValueError('foo') # no matter what kind of exception
3 except:
----> 4 dict(id=1, **{'id': 2})
KeyError: 'id'
这里发生了什么?一个完全不相关的异常如何影响dict(id=1, **{'id': 2})
抛出什么样的异常?
对于上下文,我在调查以下错误报告时发现了此行为:https://github.com/tortoise/tortoise-orm/issues/1583
这已在 CPython 3.11.8、3.10.5 和 3.9.5 上重现。
请投票给问题跟踪器 https://issuetracker.google.com/issues/330368298
我刚刚通过查看 Firebase Crashlytics 注意到我的应用程序发生了一些崩溃,这似乎与尝试使用SharedPreferences
但由于强制转换异常而无法获得首选项的 Google AdMob 库相关:
代码文字:
Fatal Exception: java.lang.ClassCastException
java.lang.Integer cannot be cast to java.lang.String
android.app.SharedPreferencesImpl.getString (SharedPreferencesImpl.java:302)
com.google.android.gms.ads.internal.scionintegration.a.onSharedPreferenceChanged (:com.google.android.gms.policy_ads_fdr_dynamite@[email protected]:73)
com.google.android.gms.ads.internal.scionintegration.m.a (:com.google.android.gms.policy_ads_fdr_dynamite@[email protected]:106)
com.google.android.gms.ads.nonagon.a.s (:com.google.android.gms.policy_ads_fdr_dynamite@[email protected]:439)
com.google.android.gms.ads.nonagon.a.b (:com.google.android.gms.policy_ads_fdr_dynamite@[email protected]:5)
com.google.android.gms.ads.ChimeraMobileAdsSettingManagerCreatorImpl.getMobileAdsSettingManager (:com.google.android.gms.policy_ads_fdr_dynamite@[email protected]:52)
com.google.android.gms.ads.internal.client.bx.bR (:com.google.android.gms.policy_ads_fdr_dynamite@[email protected]:40)
m.ajn.onTransact (:com.google.android.gms.policy_ads_fdr_dynamite@[email protected]:21)
android.os.Binder.transact (Binder.java:1173)
obj.gL (:com.google.android.gms@[email protected] (190400-607434947):8)
com.google.android.gms.ads.internal.client.ay.getMobileAdsSettingManager (:com.google.android.gms@[email protected] (190400-607434947):12)
com.google.android.gms.ads.MobileAdsSettingManagerCreatorImpl.getMobileAdsSettingManager (:com.google.android.gms@[email protected] (190400-607434947):42)
com.google.android.gms.ads.internal.client.az.fL (:com.google.android.gms@[email protected] (190400-607434947):40)
obk.onTransact (:com.google.android.gms@[email protected] (190400-607434947):101)
android.os.Binder.transact (Binder.java:1173)
com.google.android.gms.internal.ads.zzavg.zzbh (com.google.android.gms:play-services-ads-base@@22.6.0:1)
com.google.android.gms.ads.internal.client.zzcp.zze (zzcp.java:169)
com.google.android.gms.ads.internal.client.zzeq.zza (zzeq.java:169)
com.google.android.gms.ads.internal.client.zzaq.zzc (com.google.android.gms:play-services-ads-lite@@22.6.0:169)
com.google.android.gms.ads.internal.client.zzax.zzf (com.google.android.gms:play-services-ads-lite@@22.6.0:126)
com.google.android.gms.ads.internal.client.zzax.zzd (com.google.android.gms:play-services-ads-lite@@22.6.0:126)
com.google.android.gms.ads.internal.client.zzej.zzA (com.google.android.gms:play-services-ads-lite@@22.6.0:17)
com.google.android.gms.ads.internal.client.zzej.zzm (zzej.java:53)
com.google.android.gms.ads.MobileAds.initialize (com.google.android.gms:play-services-ads-lite@@22.6.0:53)
com.MY_APP_PACKAGE.ads.AdMob.initAdmob (AdMob.kt:128)
有人有这个问题吗?
我还有其他使用 22.6.0 版本 Admob 的应用程序,我没有注意到它们发生此类崩溃,而对于这个应用程序,它只发生在 6 个用户(约 23 万用户)中,但仍然......为什么会发生这种情况......
更新:
发现其他人也出现同样的问题:https ://groups.google.com/g/google-admob-ads-sdk/c/88UHAaWElsc
更新2:
现在我遇到了更多崩溃,现在它影响了其他应用程序
ps 另外,Admob Dashboard API 今天也关闭了,所以 mb 这是相关的
临时解决方案:如果您为应用程序设置了 Firebase Remote Config 以禁用广告,MobileAds.initialize()
如果您添加了特定参数,这也会阻止调用。不幸的是,就我而言,我只有参数来启用/禁用应用程序屏幕上的广告类型(横幅、插页式广告),但没有参数来阻止加载同意信息(UMP 库)和MobileAds.initialize()
,但我现在会添加它以供将来使用案例...
以下代码生成的程序集在使用-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 及更高版本中应该发生什么。