Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.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++_C++11_Perfect Forwarding - Fatal编程技术网

C++ 通用引用参数使用两次

C++ 通用引用参数使用两次,c++,c++11,perfect-forwarding,C++,C++11,Perfect Forwarding,我希望围绕std::make_对创建一个包装器,它接受一个参数并使用该参数生成该对的第一个和第二个成员。此外,我希望利用移动语义 我们可能会天真地编写(为了清晰起见,忽略返回类型) 模板 无效foo(T&T) { std::make_pair(std::forward(t), 标准:正向(t)); } 但这不太可能达到我们的目的 我们想要的是: 在使用(const)左值引用参数调用foo的情况下,我们应该将(const)引用传递给std::make_pair,而不修改这两个参数 在使用右值引

我希望围绕std::make_对创建一个包装器,它接受一个参数并使用该参数生成该对的第一个和第二个成员。此外,我希望利用移动语义

我们可能会天真地编写(为了清晰起见,忽略返回类型)

模板
无效foo(T&T)
{
std::make_pair(std::forward(t),
标准:正向(t));
}
但这不太可能达到我们的目的

我们想要的是:

  • 在使用(const)左值引用参数调用foo的情况下,我们应该将(const)引用传递给std::make_pair,而不修改这两个参数
  • 在使用右值引用参数调用foo的情况下,我们应该复制被引用的对象,然后使用原始右值引用以及新创建的对象的右值引用调用std::make_pair
到目前为止,我想到的是:

template <typename T>
T forward_or_duplicate(T t)
{
  return t;
}

template <typename T>
void foo(T&& t)
{
  std::make_pair(std::forward<T>(t),
                 forward_or_duplicate<T>(t));
}
模板
T向前或重复(T T)
{
返回t;
}
模板
无效foo(T&T)
{
std::make_pair(std::forward(t),
转发或复制(t);
}
但我有理由肯定这是错的

因此,问题是:

  • 这行吗?我怀疑不是因为如果foo()是用右值引用调用的,那么在构造传递给forward\u或\u duplicate()的T时,将调用T的move构造函数(如果存在),从而破坏T

  • 即使它确实有效,它是最优的吗?同样,我怀疑在从forward_或_duplicate()返回T时,不会调用T的copy构造函数

  • 这似乎是一个常见的问题。有惯用的解决方案吗

  • 因此,问题是:

  • 这行吗?我怀疑不是因为如果用右值引用调用foo(),那么T的move构造函数(如果存在)将是 在构造由值传递给的T时调用 转发_或_duplicate(),从而销毁t
  • 否,
    foo
    中的
    t
    是一个左值,因此构造通过值传递给的
    t
    from
    t
    调用复制构造函数

  • 即使它确实有效,它是最优的吗?同样,我怀疑在从返回T时不会调用T的复制构造函数 转发或复制()
  • 否,
    t
    是一个函数参数,因此返回隐式移动,不复制

    也就是说,此版本将更加高效和安全:

    template <typename T>
    T forward_or_duplicate(std::remove_reference_t<T>& t)
    {
      return t;
    }
    
    模板
    转发或复制(标准:删除引用)
    {
    返回t;
    }
    

    如果
    T
    是左值引用,则会产生与前面相同的签名。如果
    T
    不是参考,这将为您节省移动时间。此外,它将
    T
    放入一个非推断上下文中,这样您就不会忘记指定它。

    您的确切代码可以工作。它的细微变化(即,不调用
    make_pair
    ,而是调用一些其他函数)会导致未指定的结果。即使它看起来有效,远离这行代码(本地正确)的细微更改也会破坏它

    您的解决方案不是最优的,因为它可以将
    t
    复制两次,即使它可以工作,但只需要复制一次


    这是迄今为止最简单的解决方案。它不会修复其他地方的代码更改导致的细微中断,但如果您真的要调用
    make\u pair
    ,则不必担心:

    template <typename T>
    void foo(T&& t) {
      std::make_pair(std::forward<T>(t),
                 static_cast<T>(t));
    }
    
    但这阻碍了一个理论上的省略机会(这里没有)

    通常,在与
    static\u cast(t)
    相同的函数调用上调用
    std::forward(t)
    或任何等效的复制或转发函数都是一个坏主意。未指定参数求值的顺序,因此如果使用
    std::forward(t)
    的参数不是
    t&
    类型,并且其构造函数看到右值
    t
    并将状态移出,则
    静态转换(t)
    可以在
    t
    的状态被移出后求值

    这里不会发生这种情况:

    template <typename T>
    void foo(T&& t) {
      T t2 = t;
      std::make_pair(std::forward<T>(t),
                 std::forward<T>(t2));
    }
    
    模板
    无效foo(T&T){
    t2=T;
    std::make_pair(std::forward(t),
    标准:正向(t2));
    }
    
    因为我们将复制或向前移动到另一行,在那里初始化
    t2


    t2=TT&
    是左值引用,
    T
    也是左值引用,
    int&t2=T不复制。

    我认为如果定义了参数求值之间的顺序(不幸的是,它没有定义),您只需使用
    std::make_pair(t,std::forward(t))
    就可以做到这一点。@KerrekSB:Hah,没有想到这一点。你应该提交一个答案:-)我实际上没有发现
    转发或复制
    @KerrekSB有任何问题,我没有看到
    make_-pair
    通过引用获取参数,因此移动发生在它内部,复制发生在您输入
    make_-pair
    @KerrekSB之前,它转发左值,复制右值。谢谢。这回答了我最初的问题。在我最初的解决方案中,我使用了“T forward_或_duplicate(typename std::enable_if::type T)”将T放入一个非推断上下文中,但为了清晰起见,在问题中省略了它。
    template <typename T>
    void foo(T&& t) {
      T t2 = t;
      std::make_pair(std::forward<T>(t),
                 std::forward<T>(t2));
    }
    
    template <typename T>
    void foo(T&& t) {
      T t2 = t;
      std::make_pair(std::forward<T>(t),
                 std::forward<T>(t2));
    }