试图更好地理解 std::forward、std::move
Trying to understand std::forward, std::move a little better
我正在编写一个基本的 class 模板。它的参数有两种参数类型。 class 的想法是将一种类型作为 const ref
而另一种作为 ref
。 class 的功能是将类型 A
转换为类型 B
,其中创建的对象最终将成为 b
。我想要 perfect-forwarding
或 move semantics
这个 class 模板的有效部分。
现在这是我目前的 class 只有基本类型,但计划使用可变结构将其扩展到任何 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::move
还是 std::forward
声明上面的构造函数,这个 class 都会自行编译。现在,当我包含它并尝试在调用其构造函数之上实例化一个对象时...如果我这样做:
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>(...)
是否在 class 中使用,它都会编译和构建。
To my better understanding it apparently seems that std::move(...)
& std::forward<T>(...)
are nearly interchangeable and do the same thing except that std::forward<T>(...)
has the extra cast involved.
现在 class 因为我只展示基本类型,所以使用 std::move
似乎更合理,但我最终可能想使用更复杂的类型,所以我想考虑到这个想法提前设计这个,所以我倾向于 std::forward<T>
完美转发。
话虽这么说,要完成这个 class 有 3 个问题连在一起。
- If I'm using either
std::move
or std::forward
in the class's constructor's member initialize list, why would I have to use them again when instantiating the template class object; wouldn't this be considered redundant? If so how would the constructor look so that the user wouldn't have to use std::move()
or std::forward<T>()
when calling this constructor?
- What would be the most general and type safe way to convert
A
to B
within this context?
- Once the above two are answered with clarity then this last part within this context with the above mentioned standard classes or other similar types would then be what in regards to implementing the
operator()()
and how would it look with respect to what has already been mentioned above?
为了完成以上 3 个耦合问题,我在这里的最终想法是,在设计过程的某个时间点,我曾考虑过使用 std::any
及其相关功能作为其可能的一部分实施过程。我不知道 std::any
是否可以在这种情况下使用,如果可以如何使用?
编辑
这里可能有几个可能的未来用途 class:
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 );
这里有很多问题对我来说似乎无法简洁地回答,但有一个很突出:
"What is the difference between std::move and std::forward?"
std::move用于将左值引用转换为右值引用,通常用于将一个左值持有的资源转移到另一个左值。
std::forward用于区分左值引用和右值引用,通常用于将右值引用类型的参数推导为左值的情况。
结果:如果你想根据传递时对象的引用类型进行分支,请使用 std::forward。如果您只想通过转换为右值来窃取左值的资源,请使用 std::move.
有关更多详细信息,我发现以下内容很有帮助:http://thbecker.net/articles/rvalue_references/section_01.html
我正在编写一个基本的 class 模板。它的参数有两种参数类型。 class 的想法是将一种类型作为 const ref
而另一种作为 ref
。 class 的功能是将类型 A
转换为类型 B
,其中创建的对象最终将成为 b
。我想要 perfect-forwarding
或 move semantics
这个 class 模板的有效部分。
现在这是我目前的 class 只有基本类型,但计划使用可变结构将其扩展到任何 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::move
还是 std::forward
声明上面的构造函数,这个 class 都会自行编译。现在,当我包含它并尝试在调用其构造函数之上实例化一个对象时...如果我这样做:
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>(...)
是否在 class 中使用,它都会编译和构建。
To my better understanding it apparently seems that
std::move(...)
&std::forward<T>(...)
are nearly interchangeable and do the same thing except thatstd::forward<T>(...)
has the extra cast involved.
现在 class 因为我只展示基本类型,所以使用 std::move
似乎更合理,但我最终可能想使用更复杂的类型,所以我想考虑到这个想法提前设计这个,所以我倾向于 std::forward<T>
完美转发。
话虽这么说,要完成这个 class 有 3 个问题连在一起。
- If I'm using either
std::move
orstd::forward
in the class's constructor's member initialize list, why would I have to use them again when instantiating the template class object; wouldn't this be considered redundant? If so how would the constructor look so that the user wouldn't have to usestd::move()
orstd::forward<T>()
when calling this constructor?- What would be the most general and type safe way to convert
A
toB
within this context?- Once the above two are answered with clarity then this last part within this context with the above mentioned standard classes or other similar types would then be what in regards to implementing the
operator()()
and how would it look with respect to what has already been mentioned above?
为了完成以上 3 个耦合问题,我在这里的最终想法是,在设计过程的某个时间点,我曾考虑过使用 std::any
及其相关功能作为其可能的一部分实施过程。我不知道 std::any
是否可以在这种情况下使用,如果可以如何使用?
编辑
这里可能有几个可能的未来用途 class:
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 );
这里有很多问题对我来说似乎无法简洁地回答,但有一个很突出:
"What is the difference between std::move and std::forward?"
std::move用于将左值引用转换为右值引用,通常用于将一个左值持有的资源转移到另一个左值。
std::forward用于区分左值引用和右值引用,通常用于将右值引用类型的参数推导为左值的情况。
结果:如果你想根据传递时对象的引用类型进行分支,请使用 std::forward。如果您只想通过转换为右值来窃取左值的资源,请使用 std::move.
有关更多详细信息,我发现以下内容很有帮助:http://thbecker.net/articles/rvalue_references/section_01.html