如何确保返回的数组元素不被复制?

huangapple go评论83阅读模式
英文:

How to ensure elements of a returned array aren't copied?

问题

在以下代码片段中(我正在尝试理解现代C++的不同之处):

auto input(auto p) {
  std::cout << p << ' ';
  long long i;
  return
    std::cin >> i
    && std::set<int>{EOF, '\n'}.contains(std::cin.get())
    && i >= 0
    ? std::optional{i}
    : std::nullopt;
}

const auto input2() {
  auto a = input("Enter the first operand:");
  if (a) {
    auto b = input("Enter the second operand:");
    if (b) return std::optional{std::pair{a.value(), b.value()}};
  }
  return decltype(input2()){};
}

void reqNat() {
    std::cout << "Operands must be natural numbers!";
}

void binary(auto f, char op) {
  auto xy = input2();
  if (xy) {
    auto &[x, y] = xy.value();
    std::cout << std::format("{} {} {} = {}", x, op, y, f(x, y));
  }
  else reqNat(); 
}

xy 是否是 xy 的子对象?如果是,input2 返回类型中的 const 是否是必需的?const 有什么改变?将其放在 input 上是否是一个好主意?

是的,xyxy 的子对象。在代码中的这一行:

auto &[x, y] = xy.value();

auto &[x, y] 使用结构化绑定(structured binding)将 xy.value() 中的值绑定到 xy 上。这并不会改变 xy 的内容,但允许您更轻松地访问其中的值。

constinput2 返回类型中是可选的,它表示 input2 函数不会修改其返回的值。如果您不希望通过 xy 修改 xy 的值,您可以在 input2 返回类型中使用 const,这是一种良好的做法,以确保代码的可读性和安全性。

对于 input,是否使用 const 取决于您的需求。如果您希望确保 input 函数不修改传递给它的参数,那么您可以在 input 函数的参数中使用 const,这有助于表明这一点。但在代码示例中,input 函数并没有修改参数 p,因此 const 并不是必需的。

英文:

In the following code fragment (I'm experimenting with it to understand how modern C++ is different):

auto input(auto p) {
  std::cout &lt;&lt; p &lt;&lt; &#39; &#39;;
  long long i;
  return
    std::cin &gt;&gt; i
    &amp;&amp; std::set&lt;int&gt;{EOF, &#39;\n&#39;}.contains(std::cin.get())
    &amp;&amp; i &gt;= 0
    ? std::optional{i}
    : std::nullopt;
}

const auto input2() {
  auto a = input(&quot;Enter the first operand:&quot;);
  if (a) {
    auto b = input(&quot;Enter the second operand:&quot;);
    if (b) return std::optional{std::pair{a.value(), b.value()}};
  }
  return decltype(input2()){};
}

void reqNat() {
    std::cout &lt;&lt; &quot;Operands must be natural numbers!&quot;;
}

void binary(auto f, char op) {
  auto xy = input2();
  if (xy) {
    auto &amp;[x, y] = xy.value();
    std::cout &lt;&lt; std::format(&quot;{} {} {} = {}&quot;, x, op, y, f(x, y));
  }
  else reqNat(); 
}

are x and y subobjects of xy? If so, is the const in the return type of input2 required to ensure it? What does the const change? Would it be good to put it on input too?

答案1

得分: 1

are x and y subobjects of xy?

是的,在:

auto &amp;[x, y] = xy.value();

... xy.value() 返回对象内的左值引用,左侧的引用与之绑定。xy 随后将成为 xy 内的子对象的左值。

If so, is the const in the return type of input2 required to ensure it?

不需要,const 与此无关。在:

const auto input2() { /* ... */ }

... 这里的 const 是有害的,因为我们正在用 auto 声明一个对象,而不是 const auto。这意味着我们不必要地调用了 std::optional&lt;std::pair&lt;...&gt;&gt; 的复制构造函数,将返回的 const 对象转换为非 const 对象。

一般来说,不建议在返回类型上放置 const,因为:

  • 对于基本类型,它会被忽略
  • 对于类类型,它可能会强制进行不必要的复制

What does the const change? Would it be good to put it on input too?

它只会导致我们不必要地调用复制构造函数。
以下使用结构化绑定的方式不受 input2() 返回类型是 const 还是非 const 影响。

我们不应该在 input 上加上 const,应该从 input2 中删除 const

风格问题

请注意,您的代码中有许多不符合 "好的现代 C++" 的地方:

// 构造一个 std::set 仅用于检查一个 int 是否为两个值中的一个过于冗余。
std::set&lt;int&gt;{EOF, &#39;\n&#39;}.contains(std::cin.get())
// 考虑使用更轻量级的方法,如 IILE
[c = std::cin.get()] { return c == EOF || c == &#39;\n&#39; }()
// 或者,至少将 set 设为 'static const'


// 过多使用推导返回类型。
// 函数名和参数没有告诉我们返回类型是 std::optional&lt;long long&gt;。
// 此外,不必要使用函数模板。
// 您可以只接受 std::string_view 参数。
auto input(auto p) { /* ... */ }


// 如果必须这样做,只需使用显式返回类型。
// 这一行非常令人惊讶,可以使用带有显式类型的 return std::nullopt;。
return decltype(input2()){};
英文:

> are x and y subobjects of xy?

Yes, in:

auto &amp;[x, y] = xy.value();

... xy.value() returns an lvalue reference to the object inside, and the reference on the left hand side binds to it. x and y are then going to be lvalues which name subobjects inside of xy.

> If so, is the const in the return type of input2 required to ensure it?

No, and const has nothing to do with this. In:

const auto input2() { /* ... */ }

// ...

auto xy = input2();

// note: this assertion would pass, because xy is not const
static_assert(!std::is_const_v&lt;decltype(xy)&gt;);

... the const is harmful, because we are declaring an object with auto, not const auto. This means we are unnecessarily calling the copy constructor of std::optional&lt;std::pair&lt;...&gt;&gt; to turn the returned const object into a non-const object.

In general, it is not recommended to put const on return types, because:

  • for fundamental types, it's ignored
  • for class types, it may force unnecessary copying

> What does the const change? Would it be good to put it on input too?

It only makes us call the copy constructor unnecessarily.
The following use of structured bindings works regardless of whether input2() returns a const or non-const type.

We should not put on input, we should remove const from input2.

Stylistic Issues

Note that there are quite a few things in your code that aren't "good modern C++":

// Constructing a std::set just to check if an int is one of two values is excessive.
std::set&lt;int&gt;{EOF, &#39;\n&#39;}.contains(std::cin.get())
// Consider using something more lightweight, like an IILE
[c = std::cin.get()] { return c == EOF || c == &#39;\n&#39; }()
// alternatively, make the set &#39;static const&#39; at least


// Excessive use of deduced return types.
// The function name and parameters don&#39;t tell us anything about the return
// type being std::optional&lt;long long&gt;.
// Also, unnecessary use of function templates.
// You could have just accepted a std::string_view parameter.
auto input(auto p) { /* ... */ }


// If you have to do this, just use an explicit return type.
// This line is very surprising and could have been return std::nullopt; with
// an explicit type.
return decltype(input2()){};

huangapple
  • 本文由 发表于 2023年7月7日 02:24:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76631587.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定