参数包和移动语义
Parameter packs and move semantics
在下面的代码中,尝试通过参数包移动构造失败。
我缺少什么以及如何将代码修复为 运行 所有 4 个变体?
#include <utility>
struct File
{
File(const char *filename) {}
};
template<typename T>
struct InflateInput
{
template<typename ...Args>
InflateInput(int header, Args ...args) : source(args...) {}
T source;
};
template<typename T>
struct DeflateInput
{
template<typename ...Args>
DeflateInput(int level, int header, Args ...args) : source(args...) {}
DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}
T source;
};
int main()
{
// case 1: ok
File file{"filename"};
DeflateInput deflate1(std::move(file), 5, 0);
// case 2: ok
DeflateInput deflate2(File{"filename"}, 5, 0);
// case 3: error :-(
InflateInput<DeflateInput<File>> inflate1(0,
File{"filename"}, 9, 0);
// case 4: ok
InflateInput<DeflateInput<File>> inflate2(0,
9, 0,
"filename");
return 0;
};
编译器错误是 (-std=c++2a) 如下:
1.cpp: In instantiation of 'InflateInput<T>::InflateInput(int, Args ...) [with Args = {File, int, int}; T = DeflateInput<File>]':
1.cpp:35:26: required from here
1.cpp:13:58: error: no matching function for call to 'DeflateInput<File>::DeflateInput(File&, int&, int&)'
InflateInput(int header, Args ...args) : source(args...) {}
^
缺少完美转发。试试下面
template<typename ...Args>
InflateInput(int header, Args&& ...args) : source(std::forward<Args&&>(args)...) {}
下面的构造函数接受 T
类型的右值引用。但是 InflateInput
正在调用一个参数(Args
),它是一个左值。因此编译器错误。
DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}
您可以像这样重现相同的错误,
DeflateInput deflate3(file, 5, 0)
你这里的问题是你没有正确转发参数。
InflateInput<DeflateInput<File>> inflate1(0, File{"filename"}, 9, 0);
调用构造函数
template<typename ...Args>
InflateInput(int header, Args ...args) : source(args...) {}
其中 Args...
是 File, int, int
。因为 args...
有一个名字,整个包都是一个左值,但是你的 DeflateInput
只接受来自
的 File
的右值引用
DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}
最简单的解决方法是在 args
上调用 std::move
,例如
InflateInput(int header, Args ...args) : source(std::move(args)...) {}
但您真正应该做的是使用转发引用和 std::forward
来完美转发所有参数。这会将构造函数更改为
InflateInput(int header, Args&& ...args) : source(std::forward<Args>(args)...) {}
在下面的代码中,尝试通过参数包移动构造失败。
我缺少什么以及如何将代码修复为 运行 所有 4 个变体?
#include <utility>
struct File
{
File(const char *filename) {}
};
template<typename T>
struct InflateInput
{
template<typename ...Args>
InflateInput(int header, Args ...args) : source(args...) {}
T source;
};
template<typename T>
struct DeflateInput
{
template<typename ...Args>
DeflateInput(int level, int header, Args ...args) : source(args...) {}
DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}
T source;
};
int main()
{
// case 1: ok
File file{"filename"};
DeflateInput deflate1(std::move(file), 5, 0);
// case 2: ok
DeflateInput deflate2(File{"filename"}, 5, 0);
// case 3: error :-(
InflateInput<DeflateInput<File>> inflate1(0,
File{"filename"}, 9, 0);
// case 4: ok
InflateInput<DeflateInput<File>> inflate2(0,
9, 0,
"filename");
return 0;
};
编译器错误是 (-std=c++2a) 如下:
1.cpp: In instantiation of 'InflateInput<T>::InflateInput(int, Args ...) [with Args = {File, int, int}; T = DeflateInput<File>]':
1.cpp:35:26: required from here
1.cpp:13:58: error: no matching function for call to 'DeflateInput<File>::DeflateInput(File&, int&, int&)'
InflateInput(int header, Args ...args) : source(args...) {}
^
缺少完美转发。试试下面
template<typename ...Args>
InflateInput(int header, Args&& ...args) : source(std::forward<Args&&>(args)...) {}
下面的构造函数接受 T
类型的右值引用。但是 InflateInput
正在调用一个参数(Args
),它是一个左值。因此编译器错误。
DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}
您可以像这样重现相同的错误,
DeflateInput deflate3(file, 5, 0)
你这里的问题是你没有正确转发参数。
InflateInput<DeflateInput<File>> inflate1(0, File{"filename"}, 9, 0);
调用构造函数
template<typename ...Args>
InflateInput(int header, Args ...args) : source(args...) {}
其中 Args...
是 File, int, int
。因为 args...
有一个名字,整个包都是一个左值,但是你的 DeflateInput
只接受来自
File
的右值引用
DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}
最简单的解决方法是在 args
上调用 std::move
,例如
InflateInput(int header, Args ...args) : source(std::move(args)...) {}
但您真正应该做的是使用转发引用和 std::forward
来完美转发所有参数。这会将构造函数更改为
InflateInput(int header, Args&& ...args) : source(std::forward<Args>(args)...) {}