参数包和移动语义

时间:2019-01-15 21:16:33

标签: c++ c++17 variadic-templates perfect-forwarding

在以下代码中,尝试通过参数包进行移动构造失败。

我所缺少的东西以及如何修复运行这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...) {}
                                                          ^

2 个答案:

答案 0 :(得分:3)

缺少完美的转发。请尝试以下

template<typename ...Args>
InflateInput(int header, Args&& ...args) : source(std::forward<Args&&>(args)...) {}

以下构造函数接受类型T的r值引用。但是InflateInput正在使用参数(Args)进行调用,该参数是一个左值。因此出现编译器错误。

DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}

您可以重现相同的错误,

DeflateInput deflate3(file, 5, 0)

https://gcc.godbolt.org/z/Oe2q68

答案 1 :(得分:2)

这里的问题是您没有正确转发参数。

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仅接受对来自{p>的File的右值引用

DeflateInput(T &&stream, int level, int header) : source(std::move(stream)) {}

最简单的解决方法是像在std::move上调用args

InflateInput(int header, Args ...args) : source(std::move(args)...) {}

但是您真正应该做的是将转发引用与std::forward一起使用,以完美地转发所有参数。这样会将构造函数更改为

InflateInput(int header, Args&& ...args) : source(std::forward<Args>(args)...) {}