【问题标题】:Trying to understand std::forward, std::move a little better试图更好地理解std::forward,std::move
【发布时间】:2018-07-05 09:25:51
【问题描述】:

我正在编写一个基本的类模板。它的参数有两种参数类型。该类的想法是将一种类型输入为const ref,另一种输入为ref。该类的功能是将类型A 转换为类型B,其中创建的对象最终将是b。我想让perfect-forwardingmove semantics 成为此类模板的有效部分。


现在这是我目前的课程,只有基本类型,但计划使用可变参数结构将其扩展到任何 2 种类型。

#ifndef CONVERTER_H
#define CONVERTER_H

#include <utility>

template<class From, class To>
class Converter {
private:
    From in_;
    To   out_;

public:
    // Would like for From in to be a const (non modifiable) object
    // passed in by perfect forwarding or move semantics and for 
    // To out to be returned by reference with perfect forwarding 
    // or move semantics. Possible Constructor Declarations - Definitions

    // Using std::move
    Converter( From&& in, To&& out ) :
        in_{ std::move( in ) },
        out_{ std::move( out ) } 
    {
        // Code to convert in to out
    }

    // Or using std::forward
    Converter( From&& in, To&& out ) :
        in_{ std::forward<From>( in ) },
        out_{ std::forward<To>( out ) } {

        // Code to convert in to out.
     }        

    // Pseudo operator()... 
    To operator()() {
        return out_;
    }
};

#endif // !CONVERTER_H

无论我用std::movestd::forward 声明上面的构造函数,这个类都会自行编译。现在,当我包含这个并尝试在调用其构造函数之上实例化一个对象时......如果我这样做:

int i = 10;
float f = 0;  
Converter<int, float> converter( i, f );

这在这两种情况下都会在 Visual Studio 2017 中给我一个编译器错误。

1>------ Build started: Project: ExceptionManager, Configuration: Debug Win32 ------
1>main.cpp
1>c:\users\skilz80\documents\visual studio 2017\projects\exceptionmanager\exceptionmanager\main.cpp(54): error C2664: 'Converter<unsigned int,float>::Converter(Converter<unsigned int,float> &&)': cannot convert argument 1 from 'unsigned int' to 'unsigned int &&'
1>c:\users\skilz80\documents\visual studio 2017\projects\exceptionmanager\exceptionmanager\main.cpp(54): note: You cannot bind an lvalue to an rvalue reference
1>Done building project "ExceptionManager.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

这很容易理解{can't bind lvaule to rvalue ref}。


但是,如果我尝试像这样使用构造函数:

int i = 10;
float f = 0;
Converter<int,float> converter( std::move( i ), std::move( f ) );

// Or
Converter<int,float> converter( std::forward<int>( i ), std::forward<float>( f ) );

无论在类中使用std::move(...) 还是std::forward&lt;T&gt;(...),它都会编译和构建。


据我更好理解,std::move(...)std::forward&lt;T&gt;(...) 似乎几乎可以互换并且做同样的事情,只是 std::forward&lt;T&gt;(...) 涉及额外的演员。


目前,由于我只展示基本类型,所以现在使用 std::move 似乎更合理,但是我最终可能想要使用更复杂的类型,所以我想提前设计它考虑到这一点,所以我倾向于std::forward&lt;T&gt; 以获得完美的转发。


话虽如此,要完成这门课,有 3 个问题结合在一起。

  • 如果我在类的构造函数的成员初始化列表中使用std::movestd::forward,为什么在实例化模板类对象时必须再次使用它们;这不会被认为是多余的吗?如果是这样,构造函数的外观如何,以便用户在调用此构造函数时不必使用std::move()std::forward&lt;T&gt;()
  • 在这种情况下,将A 转换为B 的最通用且类型安全的方法是什么?
  • 一旦清楚地回答了上述两个问题,那么在此上下文中使用上述标准类或其他类似类型的最后一部分将是关于实现 operator()() 的内容,以及关于已完成的内容的外观上面已经提到了吗?

为了完成以上三个耦合问题,我的最终想法是,在设计过程的某个时间点,我曾考虑过使用std::any 及其相关功能作为其实现过程的可能部分。我不知道std::any 是否可以在这种情况下使用,如果可以,如何使用?


编辑

这可能是这个类的几个可能的未来用途:

vector<int> vecFrom{1,2,3, ...};
set<int>    setTo;

Converter<vector<int>, set<int>> converter( vecFrom, setTo );

或者展开后……

vector<int>     vecIntFrom{1,2,3, ...};
vector<string>  vecStringFrom{ "a", "b", "c", ... };
map<int,string> mapTo;
Converter<vector<int>, vector<string>, map<int,string> converter( vecIntFrom, vecStringFrom, mapTo );

【问题讨论】:

  • T&amp;&amp; 仅在使用模板类型推断时才是转发引用。从您的代码Converter&lt;int, float&gt; converter(i, f) 来看,没有发生类型推导,因此这两个参数最终成为右值引用。
  • @MárioFeroldi 好的,这给出了一点清晰并且确实有道理,但我仍在努力弄清楚如何实现not having 使用或调用std::move 的能力,@ 987654357@一般调用构造函数时。
  • std::move 是这种情况下的更好选择。 std::forward 的目的是与推导的模板参数参数一起使用
  • 它们都编译成相同的东西(当 T 不是左值引用时),问题在于记录意图
  • 阅读 Peter Gottschling 的 Discovering Modern C++ 和 Scott Meyers 的 Effective Modern C++ 等书籍是个好主意。在我看来,这是全面了解现代 C++ 的最佳方式之一。

标签: c++ templates std move-semantics perfect-forwarding


【解决方案1】:

这里有很多问题对我来说似乎无法简明地回答,但有一个问题很突出:

“std::move 和 std::forward 有什么区别?”

std::move 用于将左值引用转换为右值引用,通常将一个左值持有的资源转移到另一个。

std::forward 用于区分左值和右值引用,通常在右值引用类型的参数被推导为左值的情况下。

结果:如果您想根据传递时对象的引用类型进行分支,请使用 std::forward。如果你只是想通过转换为右值来窃取左值的资源,请使用 std::move。

有关更多详细信息,我发现以下内容很有帮助:http://thbecker.net/articles/rvalue_references/section_01.html

【讨论】:

    猜你喜欢
    • 2016-07-08
    • 2015-05-03
    • 2016-03-21
    • 1970-01-01
    • 2013-06-25
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多