为什么没有为函数参数省略副本

Mat*_*cer 3 c++ g++ c++17

我有以下代码

#include <cstdlib>
#include <vector>
#include <chrono>
#include <iostream>

static const uint64_t BENCHMARK_RUNS(1000000);

std::vector<float> vec_mul_no_ref(const std::vector<float> x,
                                  const std::vector<float> y) {
  if(x.size() != y.size()) {
    throw std::runtime_error("vectors are not the same size!");
  }
  std::vector<float> ans(x.size());
  for (size_t ii=0; ii<x.size(); ii++) {
    ans[ii] = x[ii] * y[ii];
  }
  return ans;
}

std::vector<float> vec_mul_ref_args(const std::vector<float>& x,
                                   const std::vector<float>& y) {
  if(x.size() != y.size()) {
    throw std::runtime_error("vectors are not the same size!");
  }
  std::vector<float> ans(x.size());
  for (size_t ii=0; ii<x.size(); ii++) {
    ans[ii] = x[ii] * y[ii];
  }
  return ans;
}

void vec_mul_all_ref(const std::vector<float>& x,
                   const std::vector<float>& y, std::vector<float>& ans) {
  if(x.size() != y.size() || y.size() != ans.size()) {
    throw std::runtime_error("vectors are not the same size!");
  }
  for (size_t ii=0; ii<x.size(); ii++) {
    ans[ii] = x[ii] * y[ii];
  }
}

void bench_vec_mul() {
  size_t vec_size(10000);
  std::vector<float> x(vec_size);
  std::vector<float> y(vec_size);
  for(size_t ii=0; ii<vec_size; ii++) {
    x[ii] = (static_cast<double>(rand()) / RAND_MAX) * 100.0;
    y[ii] = (static_cast<double>(rand()) / RAND_MAX) * 100.0;
  }
  // bench no_ref
  auto start = std::chrono::steady_clock::now();
  for (uint64_t ii=0; ii < BENCHMARK_RUNS; ii++) {
    std::vector<float> ans = vec_mul_no_ref(x, y);
  }
  auto end = std::chrono::steady_clock::now();
  double time = static_cast<double>(
                std::chrono::duration_cast<
                std::chrono::microseconds>(end-start).count());
  std::cout << "Time to multiply vectors (no_ref) = " 
            << time / BENCHMARK_RUNS * 1e3 << " ns" << std::endl;

  // bench ref_args
  start = std::chrono::steady_clock::now();
  for (uint64_t ii=0; ii < BENCHMARK_RUNS; ii++) {
    std::vector<float> ans = vec_mul_ref_args(x, y);
  }
  end = std::chrono::steady_clock::now();
  time = static_cast<double>(
                std::chrono::duration_cast<
                std::chrono::microseconds>(end-start).count());
  std::cout << "Time to multiply vectors (ref_args) = " 
            << time / BENCHMARK_RUNS * 1e3 << " ns" << std::endl;

  // bench all_ref
  start = std::chrono::steady_clock::now();
  for (uint64_t ii=0; ii < BENCHMARK_RUNS; ii++) {
    std::vector<float> ans(x.size());
    vec_mul_all_ref(x, y, ans);
  }
  end = std::chrono::steady_clock::now();
  time = static_cast<double>(
                std::chrono::duration_cast<
                std::chrono::microseconds>(end-start).count());
  std::cout << "Time to multiply vectors (all_ref) = " 
            << time / BENCHMARK_RUNS * 1e3 << " ns" << std::endl;
}

int main() {
  bench_vec_mul();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的笔记本电脑上的这段代码的一个示例输出(编译g++ -o benchmark main.cc -std=c++17 -O3)是:

Time to multiply vectors (no_ref) = 5117.05 ns                                      
Time to multiply vectors (ref_args) = 3000.69 ns
Time to multiply vectors (all_ref) = 2996.84 ns
Run Code Online (Sandbox Code Playgroud)

第二次和第三次如此相似表明正在执行返回值优化并且正在省略该复制.我的问题是:为什么编译器也没有复制函数参数的副本,以便第一次匹配后两个?将函数参数声明为const可确保它们不会更改,因此不会意外编辑原始变量.

我有gcc(GCC)8.1.1 20180531

Nic*_*las 5

我的问题是:为什么编译器也没有复制函数参数的副本,以便第一次匹配后两个?

因为标准不允许它们.

Elision不是刚刚发生的事情; 它不是无所不在的as-if规则的一部分.因为它影响用户可见的行为(复制/移动构造函数可能是用户定义的代码),所以标准必须明确说明有时,实现可以自由地不调用它们.因此,该标准允许在非常特定的情况下进行省略.

当从参数表达式初始化参数时,仅当参数是值类型且参数表达式是该类型的临时表时才允许使用elision.好吧,如果你想获得技术,在C++ 17中没有这种情况下的省略:prvalue将直接初始化参数而不是显示临时,因此没有复制/移动到elide.

但事实并非如此.x并且y不是prvalues,因此没有资格获得elision.它们将被复制到这些参数中.

实际上,只有当您返回时,命名对象才有资格获得省略.即使这样,如果它是该函数的参数,它也不起作用.