【问题标题】:Variadic template taking reference to data types引用数据类型的可变参数模板
【发布时间】:2018-06-27 14:38:20
【问题描述】:

我想为类方法创建一个包装器,因此我使用了变量模板和 std::tuple 来包装该方法。它可以按我的需要工作,但我无法弄清楚如何能够使用通过引用调用的参数来包装方法。请看下面的主要功能。

#include <iostream>
#include <string>
#include <functional>

using namespace std::placeholders;

namespace helper {

template <std::size_t... Ts> struct index {};

template <std::size_t N, std::size_t... Ts> struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};

template <std::size_t... Ts> struct gen_seq<0, Ts...> : index<Ts...> {};

template<class F, class... Ts, std::size_t... Is> void for_each_in_tuple(std::tuple<Ts...> &tuple, F func, std::index_sequence<Is...>)
{
    using expander = int[];
    (void)expander {
        0, ((void)func(std::get<Is>(tuple)), 0)...
    };
}

template<class F, class...Ts> void for_each_in_tuple(std::tuple<Ts...> &tuple, F func)
{
    for_each_in_tuple(tuple, func, std::make_index_sequence<sizeof...(Ts)>());
}
}

enum class Result : std::int32_t {
    Result1 = 1,
    Result2 = 2
};

template<typename... Args> class Procedure {
private:

    std::function<Result(Args...)> m_method;
    std::tuple<Args...> args;

    template <typename... Args, std::size_t... Is> Result callMethod(std::tuple<Args...> &tup, helper::index<Is...>)
    {
        return  m_method(std::get<Is>(tup)...);
    }

    template <typename... Args> Result callMethod(std::tuple<Args...> &tup)
    {
        return callMethod(tup, helper::gen_seq<sizeof...(Args)> {});
    }

public:

    template <typename ...Args> void setMethod(const std::function<Result(Args &&... args)> &method)
    {
        m_method = method;
    }
};

class MethodsClass {

public:

    Result doSomething(std::int32_t input, double output)
    {
        return Result::Result1;
    }
};

int main()
{
    **// This works fine**
    MethodsClass methods;
    Procedure <std::int32_t, double> procedure1;
    procedure1.setMethod<std::int32_t, double>(std::bind(&MethodsClass::doSomething1, methods, _1, _2));

    **// This end with compiler error std::tuple<int32_t,double &>::tuple': no appropriate default constructor available**

    Procedure <std::int32_t, double &> procedure2;
    procedure2.setMethod<std::int32_t, double &>(std::bind(&MethodsClass::doSomething2, methods, _1, _2));

    return 0;
}

我知道为什么第二种情况无法编译,消息很清楚,很明显必须初始化引用。但是在这种情况下该怎么做呢?如何初始化 std:tuple,如何以及在哪里扩展参数包?我错过了一些东西,不知道是什么。

【问题讨论】:

  • 您可能想了解标准库如何使用std::reference_wrapper(这是由std::refstd::cref 创建的对象)。
  • 有问题的std::tuple&lt;Args...&gt; args;没有使用...
  • std::function&lt;Result(Args &amp;&amp;... args)&gt; 应该是 std::function&lt;Result(Args... args)&gt; 甚至是 F&amp;&amp; f
  • 你在你的方法中隐藏你的类Args...,最好使用不同的名字......
  • 您的代码似乎没有足够注意调用的右值/左值上下文。如果我设置了一个过程,调用它的合法性和效率将取决于我是调用它并丢弃参数(即,从参数中移动)还是调用它并期望参数持续存在(也许我可以调用它不止一次)。二、你考虑过std::function&lt;void()&gt;吗?

标签: c++ c++11 pass-by-reference variadic-templates stdtuple


【解决方案1】:

嗯,在 C++17 中这很容易。必须声明元组:

std::tuple<typename std::decay<Args>::type...> m_args;

那么所有的递归元组解包都可以简单的去掉,替换为:

std::apply(m_method, m_args);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 2014-04-19
    • 2016-01-22
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 1970-01-01
    相关资源
    最近更新 更多