试图理解std :: forward,std :: move更好

时间:2018-01-26 01:27:48

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

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

现在这里是我目前只有基本类型的类,但计划使用可变参数构造将其扩展为任何两种类型。

#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<T>(...),它都会进行编译和构建。

  

据我所知,std::move(...)&amp; std::forward<T>(...)几乎是可以互换的,除了std::forward<T>(...)涉及额外的演员之外,它会做同样的事情。

现在因为我只显示基本类型,所以使用std::move似乎更合理,但我最终可能想要使用更复杂的类型,所以我想提前设计这个类考虑到这种想法,所以我倾向于std::forward<T>以获得完美的转发。

有了这个说法并完成这个课程,有3个问题加在一起。

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

要完成上述3个耦合问题,我最后的想法是,在设计过程的某个时刻,我曾考虑过使用std::any及其相关功能作为其实施过程的可能部分。我不知道在这种情况下是否可以使用std::any,如果可以,如何使用?{1}}

修改

这个类可能有以下几种可能的用途:

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 );

1 个答案:

答案 0 :(得分:2)

这里有很多问题对我来说似乎并不简单,但有一个问题突出:

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

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

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

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

有关详细信息,我发现以下内容非常有用:http://thbecker.net/articles/rvalue_references/section_01.html