Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/132.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 通过右值引用而不是通过值获取接收器参数以强制接口的性能使用_C++ - Fatal编程技术网

C++ 通过右值引用而不是通过值获取接收器参数以强制接口的性能使用

C++ 通过右值引用而不是通过值获取接收器参数以强制接口的性能使用,c++,C++,在一次代码审查中,我的同事和我讨论了我正在编写的函数的接口。我们的代码库使用C++17,不使用异常(视频游戏) 我声称,采用CUN参数的惯用C++方式是性能化的,同时也保持接口灵活,允许调用方通过拷贝或从所需的值中移动。按照惯用的方式,我指的是一个按值获取参数的函数,或者是一个const-lvalue和rvalue引用的重载集(这在左值情况下需要较少的移动,代价是一些代码重复) 调用方知道f已取得x的所有权。与 void g(T&& x); g可能拥有所有权,也可能没有所有权,

在一次代码审查中,我的同事和我讨论了我正在编写的函数的接口。我们的代码库使用C++17,不使用异常(视频游戏)

我声称,采用CUN参数的惯用C++方式是性能化的,同时也保持接口灵活,允许调用方通过拷贝或从所需的值中移动。按照惯用的方式,我指的是一个按值获取参数的函数,或者是一个const-lvalue和rvalue引用的重载集(这在左值情况下需要较少的移动,代价是一些代码重复)

调用方知道
f
已取得
x
的所有权。与

void g(T&& x);
g
可能拥有所有权,也可能没有所有权,这取决于
f
的实现


有没有最好的办法?我是不是漏掉了一些参数?

你基本上有这些构造函数选项:

class myclass {
public:
    // #1 myclass(const std::string& s) : s(s) {}
    // #2 myclass(std::string&& s) : s(std::move(s)) {}

    // #3 myclass(std::string s) : s(std::move(s)) {}

    // #4 template <typename T> myclass(T&& t) : s(std::forward<T>(t)) {}

    std::string s;
};
以下是复制/移动构造函数的计数

                        | A | B | C | D | E |
------------------------|---|---|---|---|---|
1                  Copy |<1>| 2 | 1 | 1 | 1 |
const l-value ref  Move |<0>| 1 | 1 | 0 | 0 |
                  Other |<0>| 0 | 0 | 1 | 1 |
------------------------|---|-v-|-v-|-v-|-v-|
2                  Copy | X |<1>|<0>| 0 |<0>|
r-value ref        Move | X |<1>|<1>| 1 |<1>|
                  Other | X |<0>|<0>| 1 |<1>|
------------------------|---|---|---|---|---|
3                  Copy | 1 | 1 | 0 | 0 |<0>|
by value           Move | 1 | 2 | 2 | 1 |<1>|
                  Other | 0 | 0 | 0 | 1 |<1>|
------------------------|---|---|---|---|---|
4                  Copy |<1>|<1>|<0>|<0>| X |
Forwarding ref     Move |<0>|<1>|<1>|<0>| X |
                  Other |<0>|<0>|<0>|<1>| X |
--------------------------------------------/
| A | B | C | D | E|
------------------------|---|---|---|---|---|
1份| 2 | 1 | 1 | 1|
常数l值参考移动| | 1 | 1 | 0 | 0|
其他| 0 | 0 | 1 | 1|
------------------------|---|-v-|-v-|-v-|-v-|
2份| X | | 0||
r值参考移动| X | | | 1||
其他| X | | 1||
------------------------|---|---|---|---|---|
3份| 1 | 1 | 0 | 0||
按值移动| 1 | 2 | 2 | 1||
其他| 0 | 0 | 0 | 1||
------------------------|---|---|---|---|---|
4份复印件|
转发参考移动|
其他|
--------------------------------------------/
可能的配置:

  • #1
    仅限:处理所有案例,但复制用于临时
  • #1/#2
    :(B/C/D/E将使用
    #2
    ),因此除了就地施工外,效果最好
  • #3
    仅限:处理所有情况,但执行额外移动
  • #4
    仅限:处理大多数常规案例,获得最佳结果
  • #1/#2/#4
    :最佳结果(请注意,
    #4
    )与非常量l值完全匹配)
  • #2/#4
    :最佳结果
  • 仅限
    #2
    禁止复制,但显式复制(B)比
    #1
    /
    #2
    多移动1步
如你所见:

  • 转发引用(
    #4
    )的效果最好
  • By const ref(
    #1
    )的复制性能最好,但其他复制性能较差
  • 然后按值(
    #3
    )是第二个“最差”,但只比最好的多了一步
其他要点:

  • C++11之前只有
    #1
    可用(大多数界面的默认设置也是如此)
  • 只有
    #1
    可能意味着没有所有权转让
  • 仅移动(
    #2
    )禁止隐式复制
  • By Value
    #3
    的目的是只编写一个重载作为良好的折衷
现在比较
#1
/
#2
#2
#3

  • 对于仅移动类型:

    • #1
      /
      #2
      是不相关的
    • #3
      立即下沉
    • #2
      有机会处理异常保证:如果抛出,则不需要使用对象
    除非您想要保证和/或立即接收,否则我将使用pass-by-rvalue(
    #2

    对于现有的代码库,我将保持一致性

对于可复制类型: -
#1
/
#2
是最有效的,但允许不需要的复制。 -
#3
很方便(而且只需一次额外移动),但允许不需要的复制,保证接收。 -
#2
避免不需要的拷贝

现在,这主要是您想要保证和允许的:

  • 最佳性能->
    #1/#2
  • 无(隐式)副本->
    #2
  • 即时/保证接收->
    #3
  • 与仅可移动类型的一致性

    对于现有的代码库,我将保持一致性


在大多数情况下,通过值传递将由编译器进行完美优化,并且值语义对于屏幕后面编写代码的人来说更容易推理。如果可能的话,我会选择传递值,除非你的分析器告诉你一个不这样做的真正理由。除非您已经证明需要这样做,否则不要将其复杂化。您的最终目标是什么,以获得最佳性能?这完全是一个API设计问题,可能只是一个意见问题。当与移动语义一起使用时,Pass-by-value可能同样快。问题是,您是否希望向用户强制要求他们制作自己的副本,并在需要时移动副本,以避免无意中复制副本。您可能会争辩说,在副本损坏的地方更安全,如果这不是一种常见情况,您可能会争辩说这很不方便。@JesperJuhl Pass-by-value在大多数情况下会被编译器完美地优化真的吗?我知道按值返回是正确的,但在大多数情况下,按值传递AFAIK会留下很多性能问题。另一种说法是:你的同事的论点是“我的同事不喜欢隐式制作昂贵的拷贝。”如果这是一个问题,你的同事是对的。这取决于您的项目或产品的特殊情况。在我看来,这个问题没有一个客观的答案。
void g(T&& x);
class myclass {
public:
    // #1 myclass(const std::string& s) : s(s) {}
    // #2 myclass(std::string&& s) : s(std::move(s)) {}

    // #3 myclass(std::string s) : s(std::move(s)) {}

    // #4 template <typename T> myclass(T&& t) : s(std::forward<T>(t)) {}

    std::string s;
};
std::string s;
myclass A(s);
myclass B(std::string(s));
myclass C(std::move(s));
myclass D("temporary");
myclass E({5, '*'});
                        | A | B | C | D | E |
------------------------|---|---|---|---|---|
1                  Copy |<1>| 2 | 1 | 1 | 1 |
const l-value ref  Move |<0>| 1 | 1 | 0 | 0 |
                  Other |<0>| 0 | 0 | 1 | 1 |
------------------------|---|-v-|-v-|-v-|-v-|
2                  Copy | X |<1>|<0>| 0 |<0>|
r-value ref        Move | X |<1>|<1>| 1 |<1>|
                  Other | X |<0>|<0>| 1 |<1>|
------------------------|---|---|---|---|---|
3                  Copy | 1 | 1 | 0 | 0 |<0>|
by value           Move | 1 | 2 | 2 | 1 |<1>|
                  Other | 0 | 0 | 0 | 1 |<1>|
------------------------|---|---|---|---|---|
4                  Copy |<1>|<1>|<0>|<0>| X |
Forwarding ref     Move |<0>|<1>|<1>|<0>| X |
                  Other |<0>|<0>|<0>|<1>| X |
--------------------------------------------/