提问者:小点点

C++参数作为常量T与常量T的关系&


假设someDataStruct是“巨大的”,那么在以下两种情况下,一个现代编译器会产生等价的,因此也是同样有效的代码吗?

1) void fooByValue(const SomeDataStruct data);   

2) void fooByReference(const SomeDataStruct& data);

如果等价,哪一个成语是“可取”的,为什么?

请注意,这道题与这道题类似:

https://softwareengineering.stackexchange.com/questions/372105/is-passing-arguments-as-const-references-premater-optimization

但不完全相同,因为这里我在两个函数中都使用“const”; 在所提到的链接中,FoobyValue只是

1) void fooByValue(SomeDataStruct data);

编辑:假设someDataStruct类型没有任何复制构造函数。


共2个答案

匿名用户

假设某个DataStruct是“巨大的”,那么在以下两种情况下,一个现代编译器会产生等价的,因此也是同样高效的代码吗?

这取决于编译器,你是如何定义对象的,函数中的对象发生了什么。 让我们了解gcc在x86-64 Linux上的作用。 看一看下面的代码:

struct big_
{
    unsigned long w,x,y,z;
    int pos;
};

unsigned long byval(const big_ big)
{
    auto y = big.z;
    y += big.y;
    y += big.x;
    y += big.w;
    return y;
}

unsigned long byref(const big_& big)
{
    auto y = big.z;
    y += big.y;
    y += big.x;
    y += big.w;
    return y;
}

g++-std=C++17-o3编译它会得到以下程序集Godbolt链接:

byval(big_):
        mov     rax, QWORD PTR [rsp+24]
        add     rax, QWORD PTR [rsp+32]
        add     rax, QWORD PTR [rsp+16]
        add     rax, QWORD PTR [rsp+8]
        ret
byref(big_ const&):
        mov     rax, QWORD PTR [rdi+16]
        add     rax, QWORD PTR [rdi+24]
        add     rax, QWORD PTR [rdi+8]
        add     rax, QWORD PTR [rdi]
        ret

在上面的代码中我们能看到什么呢?

第一个函数byval的参数在堆栈上传递。 第二个函数的参数通过寄存器rdi传递(请参阅操作系统调用约定以了解原因)。 通过寄存器传递任何东西总是比通过堆栈传递更快,因为寄存器更靠近CPU,而堆栈在缓存或RAM中的某个地方。 因此这里通过引用传递更好。 如果您有一个小对象(8字节),通过值传递它更好,因为它将通过寄存器传递。 只有当对象太大以至于无法容纳寄存器时,才会通过堆栈传递。

我们可以看到的另一件事是byval有一个不是const的参数。 编译器刚刚删除了它。

因为这里我在两个函数中都使用了“const”;

从上面的解释中可以看出,这并不重要。

void fooByValue(SomeDataStruct data);edit:假设SomeDataStruct类型没有任何复制构造函数。

如果您没有编写复制构造函数,这并不意味着编译器不能隐式地为您生成一个。 但我们假设你把它删掉了。 所以现在您不能复制它,如果您尝试,您的代码将无法编译。 但是,如果您已经定义了移动构造函数,则可以移动它。

void fn()
{
    fooByValue( std::move(data) ); // no copy
}

使用常量不会更改代码的性能。 const就像是您和编译器之间的契约。 当您将某事标记为const时,编译器将不允许您更改它。 如果你以某种方式改变它,它将导致未定义的行为。 我建议你去读一下这篇由阿瑟·奥德怀尔写的关于const是一份合同的文章。

匿名用户

顶级常量(不在指针/引用之后)在声明中不起作用。

// Even if the declaration uses const:
void fooByValue(const SomeDataStruct data);

// The definition without `const` is still considered a valid definition for that declaration.
// It's not a different overload!
// (this oddity is inherited from C, where overloading is a compiler error but this was always allowed)
void fooByValue(SomeDataStruct data)
{
   data.member = 0;
}

因此编译器被迫忽略常量,并且必须假定参数可能被修改-->; 副本是必要的。 但是,如果在函数调用之后没有使用原始的copy-from变量,编译器仍然可能优化出副本。

C++标准的相关部分是9.3.3.5函数:

函数的类型是使用以下规则确定的。 [..]在生成参数类型列表之后,在形成函数类型时删除修改参数类型的任何顶级CV限定符。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++|参数|常量|t|常量|t|关系)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?