对 int 的本地引用有什么好处吗?

Does local reference to int provide any benefit?

考虑以下两个代码示例:

void int_fun(int val) {}

void another_fun() {
    vector<int> test(1, 1);
    const int& int_ref = test[0];
    int_fun(int_ref);
}

对比

void int_fun(int val) {}

void another_fun() {
    vector<int> test(1, 1);
    const int int_val = test[0];
    int_fun(int_val);
}

编译器是否可以将 int_ref 实现为无操作,而在第二种情况下它必须创建 test[0] 的副本?或者在第二种情况下也可以优化复制,这两个样本在性能上是等效的?

示例和问题的动机是,有时为了代码清晰起见,创建将立即传递给示例中的某些函数的本地对象是有益的。

在这种情况下,对基本类型(如 int、bool 等)使用引用是否有任何好处,或者引用和值是等价的(或者值可能更好)?

使用对局部值的引用有时很有用,即使它们是基本类型,如果它们在更复杂的类型中,这样您就可以将引用用作实际变量的 read/write 快捷方式。这里的技巧是 write 部分。如果不需要修改变量,那么复制值也一样。

以更复杂的代码为例:

void fun()
{
     vector<int> test;
     //fill test with a lot of values
     for (int i=0; i < test.size(); ++i)
     {
         int &x = test[i]; //<-- handy reference!
         if (x > i)
             ++x;
         else if (x < i)
             --x;                
     }
}

如果你想在没有引用的情况下编写相同的逻辑,你必须至少使用 vector::operator[](int) 两次!

vector改成map,事情就更有趣了:

void fun()
{
    map<string, int> keys;
    //fill keys
    int &x = keys["foo"];
    ++x;
}

为了清晰起见,代码。对于重要代码,有时使用对容器成员的引用可以使代码更清晰。此外,我可能会在将智能指针传递给函数时执行此操作。在函数内部,我将通过将其分配给一个引用来取消引用一次,并在整个函数中使用该引用。 这样做的目的是使它更清晰并避免复制对象。对于内置类型,我只使用赋值并避免引用。

如果你真的需要每一次的性能,那么你可以看看产生的汇编语言代码。然后,对其进行分析。

在你给的情况下,我看不出真正的优势。我怀疑大多数编译器在大多数情况下都会为两者生成相同的代码(至少在启用优化的情况下)。我认为该引用使意图更加清晰:真正的意图是将原始对象传递给函数,而您只是出于某种原因创建并传递了一个别名给该对象。

在略有不同的情况下,行为可能会有所不同。显而易见的是,如果函数收到引用:

void int_fun(int &val);

void another_fun() {
    vector<int> test(1, 1);
    int &int_val = test[0];
    int_fun(int_val);
}

在这种情况下,int_fun 可能会修改它收到引用的值,因此如果您创建了一个引用,然后通过引用传递它,引用将被折叠,因此该函数可以修改数组中的值(但如果您创建了一个副本,然后通过引用传递它,则会修改副本而不是原始副本)。

您展示的代码示例是微优化(或微去优化,不管是什么情况),因为在现代硬件上制作原语的单个副本的成本非常低;引用原语也是如此。与调用执行任何感兴趣的函数的成本相比,成本逐渐降低。

但是,对您的示例进行的一个非常简单的更改演示了一种情况,当引用原语变得有益时,因为您可以将它们分配回去。

这是您的示例,修改为使用 std::map<std::string,int> 代替 std::vector<int>:

std::map<std::string,int> cache;

int compute_new(int old) {
    return old+1;
}

void fun_with_ref(const std::string& key) {
    int& int_ref = cache[key];
    int_ref = compute_new(int_ref);
}

void fun_with_val(const std::string& key) {
    int int_val = cache[key];
    cache[key] = compute_new(int_val);
}

请注意 fun_with_ref 如何通过 key 执行单次查找,而 fun_with_val 需要两次查找。 std::map<std::string,ing> 的访问时间增长为 O(log2N),因此当地图增长到大尺寸时,节省的时间可能会变得很可观。

A quick micro-benchmark 显示使用包含 1,000,000 个条目的映射的引用的代码几乎是使用值的代码的两倍。

vector<string> keys;
for (int i = 0 ; i != 1000000 ; i++) {
    auto key = to_string(i);
    cache[key] = i;
    keys.push_back(key);
}
auto ms1 = duration_cast< milliseconds >(
    system_clock::now().time_since_epoch()
).count();
for (const string& key : keys) {
    fun_with_ref(key);
}
auto ms2 = duration_cast< milliseconds >(
    system_clock::now().time_since_epoch()
).count();
for (const string& key : keys) {
    fun_with_val(key);
}
auto ms3 = duration_cast< milliseconds >(
    system_clock::now().time_since_epoch()
).count();
cout << "Ref: " << (ms2-ms1) << endl;
cout << "Val: " << (ms3-ms2) << endl;

输出:

Ref: 557
Val: 1064