【问题标题】:Parameter packs and move semantics参数包和移动语义
【发布时间】:2019-01-15 21:16:33
【问题描述】:

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

我缺少什么以及如何修复代码以运行所有 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...) {}
                                                          ^

【问题讨论】:

  • 错误信息是什么?
  • T 在这两种情况下都是不可演绎的上下文。您需要向DeflateInput 提供模板参数。我看不出//case 1: ok// case 2: ok 的评论是如何站得住脚的,即使它甚至无法编译。此外,如果您真的想按照自己的意愿工作,这应该使用适当的转发引用。
  • 为什么DeflateInput有两个构造函数?
  • @WhozCraig 在 C++17 中被允许。
  • @Chameleon 感谢您通过添加 C++17 标记进行澄清。

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


【解决方案1】:

缺少完美转发。试试下面

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)

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

【讨论】:

    【解决方案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 只接受一个来自

    的对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)...) {}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-18
      • 1970-01-01
      • 1970-01-01
      • 2015-10-13
      • 2016-02-04
      • 2020-08-20
      • 2012-01-01
      • 1970-01-01
      相关资源
      最近更新 更多