C++:通用函数包装器 class 作为非模板 class 的成员

C++: Generic function wrapper class as a member of a non-template class

我正在尝试实现一个卡尔曼滤波器 class,它可以接受用户提供的测量模型函数,该函数可以有任意数量的参数。如果您不熟悉卡尔曼滤波器也没关系,基本上我有一个非模板 class,它有一个模板化的构造函数,它接收一个函数,该函数具有任意数量的任意类型的参数。构造函数然后将传递的函数分配给函数包装器成员对象,该对象本身是基于可变参数模板 classes 构造的。我遇到的问题是如何在不知道将传递给构造函数的函数的具体形式的情况下将函数包装器对象声明为卡尔曼滤波器 class 的成员?

如果不是很清楚,我深表歉意。但是,我基本上一直在关注 this answer to this 问题,其中行 auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3) 表示我想作为成员变量合并到我的非模板 class 中的功能(进行赋值在构造函数中)。

这是我的代码:

#include <iostream>
#include <functional>
#include <tuple>

namespace Helper
{
    template <std::size_t... Ts>
    struct Index {};

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

    template <std::size_t... Ts>
    struct GenSeq<0, Ts...> : Index<Ts...> {};
}

template <typename... ArgTps>
class MeasModWrapper
{
private:
    std::function<void(ArgTps...)> MeasMod;
    std::tuple<ArgTps...> MeasModArgs;

public:
    template <typename MsModFnc, typename... ArgTs>
    MeasModWrapper( MsModFnc&& MsMod, ArgTs&&... args )
        :    MeasMod( std::forward<MsModFnc>(MsMod) ),
             MeasModArgs( std::make_tuple( std::forward<ArgTs>(args)... ) )
    {}

    template <typename... ArgTs, std::size_t... Is>
    void MsMod( std::tuple<ArgTs...>& tup, Helper::Index<Is...> )
    {
        MeasMod( std::get<Is>(tup)... );
    }

    template <typename... ArgTs>
    void MsMod( std::tuple<ArgTs...>& tup )
    {
        MsMod( tup, Helper::GenSeq<sizeof...(ArgTs)>{} );
    }

    void CallMeasMod( void )
    {
        MsMod( MeasModArgs );
    }
};

template <typename MsModFnc, typename... ArgTs>
MeasModWrapper<ArgTs...> BindMeasMod( MsModFnc&& MsMod, ArgTs&&... args )
{
    return MeasModWrapper<ArgTs...>( std::forward<MsModFnc>( MsMod ), std::forward<ArgTs>( args )... );
}

// The following is where my code differs from the solution provided by user 0x499602D2 (many thanks btw)
class KF
{
private:
    auto measurementModel;              // <--- This doesn't work, and I expected that it wouldn't, but how can I get around this problem???

public:
    template <typename MsFnc, typename... FncArgs>
    KF( MsFnc&& msFnc, FncArgs&&... fncArgs )
        :    measurementModel( BindMeasMod( msFnc ), std::forward<FncArgs>( fncArgs )... )
    {}

    void CallMeasMod( void )
    {
        measurementModel.CallMeasMod();
    }
};

int main( void )
{
    KF kf([](int x, int y){ std::cout << (x+y) << std::endl; }, 3, 4 );         // Just a simple example function - in reality it could take any form and most likely wouldn't be a lambda function, but that's not particularly important at this stage.
    kf.CallMeasMod();
    //auto measurementModel = BindMeasMod([] (int a, int b) { std::cout << a + b << std::endl; }, 3, 4);        // <-- This would work because it by-passess the KF class - BUT it is not what I want
    //measurementModel.CallMeasMod();
    return 0;
}

我确信如果 KF class 是一个模板 class,问题可能会大大简化,但我真的不想这样做。我不知道 Boost 是否会提供某种方式来实现我想要的,但如果可能的话我更喜欢基于 STL 的解决方案。非常感谢任何可以为此提供帮助的人。

我对可变参数模板还很陌生,老实说,我仍然觉得它们有点令人困惑(例如,我不是 100% 确定 Helper 命名空间中的代码是如何工作的)所以如果有人也可以推荐一个很好地解释它们的好书或网站(对于傻瓜),那么也将不胜感激。

P.S。如果重要的话,我在 Ubuntu 14.04 上使用 g++ 4.8.4。


编辑:

我忘了说我正在尝试构建一个库。 KF class 将具有许多附加功能,但这是所有卡尔曼滤波器实现的标准。每个实现的不同之处在于测量模型(和过程模型,但我没有在这个例子中包括它,因为它将以与测量模型相同的方式处理)所以我需要能够处理任何类型的功能使用任意数量的参数,然后在幕后的 KF class 本身的不同点处调用该函数的通用方法(我在此处包含的代码显示了在主函数中调用的测量模型函数,但是实际上它不会在这里被调用。它将在 KF class 中使用,其确切形式未知)。该库将由工程师使用,该工程师将提供用作测量模型的特定函数,因此 he/she 将在 KF class 的构造期间指定函数的名称及其参数,并且预计这将是一个常规的、非模板化的函数。我在这个例子中只使用了一个简单的 lambda 函数,因为它整齐地放在一行上,而且它也在我下面的例子中使用。

不清楚这对您有什么好处,但这使您的示例有效:

#include <iostream>
#include <functional>

class KF
{
private:
    std::function<void()> measurementModel;

public:
    template <typename MsFnc, typename... FncArgs>
    KF( MsFnc&& msFnc, FncArgs&&... fncArgs )
        :    measurementModel([=]() { return msFnc(fncArgs...); })
    {}

    void CallMeasMod( void )
    {
        measurementModel();
    }
};

int main( void )
{
    KF kf([](int x, int y){ std::cout << (x+y) << std::endl; }, 3, 4 );
    kf.CallMeasMod();
    return 0;
}

Demo