通过 const 引用将参数传递给非 void 函数:它是否被视为副作用
Pass a parameter by const reference to a non-void function : is it considered as a side effect?
编写函数(不是过程(的最干净方法是什么?
第二种解决方案是否被告知有"副作用"?
struct myArea
{
int t[10][10]; // it could by 100x100...
};
解决方案1:按值传递
double mySum1(myArea a)
{
// compute and return the sum of elements
}
解决方案 2:通过常量引用传递
double mySum2(const myArea & a)
{
// compute and return the sum of elements
}
我更喜欢的是第一个(清洁功能(,尽管效果较差。但是,当有大量数据要复制时,可能会非常耗时。
感谢您的反馈。
我对你的术语有很多疑问:
-
在 C 或 C++ 中没有所谓的"过程"。 充其量,有些函数不返回任何值:"void">
-
您的示例没有"副作用"。
-
我不确定你说的"清洁功能"是什么意思......但我希望你的意思不是"更少的源代码 == 更干净的代码"。 事实并非如此:(
要回答您的原始问题:
-
在您的示例中,
double mySum1(myArea a)
会产生完全不必要的副本的空间和 CPU 开销。 不要这样做:) -
在我看来,
double mySum1(myArea & a)
或double mySum1(myArea * a)
是等价的。 就个人而言,我更喜欢double mySum1(myArea * a)
...但大多数C++开发人员(正确地!(更喜欢double mySum1(myArea & a)
。 -
double mySum1 (const myArea & a)
是最好的:它的运行时效率为 2(,并且它表明您的意图不会修改数组。
附注:我从以下测试生成了程序集输出:
struct myArea {
int t[10][10];
};
double mySum1(myArea a) {
double sum = 0.0;
for (int i=0; i < 10; i++)
for (int j=0; j<10; j++)
sum += a.t[i][j];
return sum;
}
double mySum2(myArea & a) {
double sum = 0.0;
for (int i=0; i < 10; i++)
for (int j=0; j<10; j++)
sum += a.t[i][j];
return sum;
}
double mySum3(myArea * a) {
double sum = 0.0;
for (int i=0; i < 10; i++)
for (int j=0; j<10; j++)
sum += a->t[i][j];
return sum;
}
double mySum4(const myArea & a) {
double sum = 0.0;
for (int i=0; i < 10; i++)
for (int j=0; j<10; j++)
sum += a.t[i][j];
return sum;
}
正如您所期望的那样,mySum1 有额外的代码来执行额外的复制。
然而,mySum2、mySum3 和 mySun4 的输出是相同的:
_Z6mySum2R6myArea:
.LFB1:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
movq %rdi, -32(%rbp)
movl $0, %eax
movq %rax, -24(%rbp)
movl $0, -16(%rbp)
jmp .L8
.cfi_offset 3, -24
.L11:
movl $0, -12(%rbp)
jmp .L9
.L10:
movl -16(%rbp), %eax
movl -12(%rbp), %edx
movq -32(%rbp), %rcx
movslq %edx, %rbx
movslq %eax, %rdx
movq %rdx, %rax
salq $2, %rax
addq %rdx, %rax
addq %rax, %rax
addq %rbx, %rax
movl (%rcx,%rax,4), %eax
cvtsi2sd %eax, %xmm0
movsd -24(%rbp), %xmm1
addsd %xmm1, %xmm0
movsd %xmm0, -24(%rbp)
addl $1, -12(%rbp)
.L9:
cmpl $9, -12(%rbp)
setle %al
testb %al, %al
jne .L10
addl $1, -16(%rbp)
.L8:
cmpl $9, -16(%rbp)
setle %al
testb %al, %al
jne .L11
movq -24(%rbp), %rax
movq %rax, -40(%rbp)
movsd -40(%rbp), %xmm0
popq %rbx
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
<= mySum3 and mySum4 had different labels ... but identical instructions!
还值得注意的是,"const"的好处之一是它可以帮助编译器尽可能执行几种不同类型的优化。 例如:
const 在 C/C++ 中提供了什么样的优化?(如有(
C++"康斯特"宣言:为什么和如何
C++中没有"程序"这样的东西。不返回任何内容的函数仍然是函数。
现在来问:如果你的参数是输出参数或输入/输出参数,也就是说,你希望调用方看到函数内部对传递给它的对象的更改,然后通过引用传递。否则,如果类型很小/复制起来非常便宜,请按值传递。否则,通过引用 const 传递。在你的情况下,我会通过引用 const。
参数传递本身并不是副作用。
如果一个函数做任何可观察的事情,而不仅仅是返回一个值,那将是一个副作用。
(例如修改引用参数,打印某些内容,修改任何全局状态...
也就是说,即使您通过非常量引用传递,副作用的存在也取决于您是否修改了引用的对象。
- 在提升multi_index容器中,是否定义了"default index"?
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 检查输入是否不是整数或数字
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 是否可以通过C++扩展强制多个python进程共享同一内存
- 此代码是否违反一个定义规则
- 是否需要删除包含对象的"pair"?
- 是否可以从int转换为enum类类型
- 使用运营商New分配的数据结构是否有任何副作用
- 如果 LTO 中的代码依赖于其构造的副作用,是否允许 LTO 删除未使用的全局对象?
- 成员子对象继承存储持续时间是否有任何非直观的副作用
- 使用gcc调试选项编译生产代码是否有任何副作用
- 通过 const 引用将参数传递给非 void 函数:它是否被视为副作用
- 如何确定作业中的两个副作用是否未排序
- C++是否允许优化编译器忽略对 for 条件的副作用
- 是否应该对没有副作用的已丢弃值表达式进行诊断
- 编译器是否可以优化对函数的调用,但可能会产生副作用
- 在c++程序中,分配内存然后释放内存是否构成副作用?
- 私人新运营商是否有任何意想不到的副作用