使用可变参数模板将任意数量的变量传递给函数

问题描述 投票:0回答:3

我有一个模板类,它基本上允许使用0到3种不同的类型。这些类型在构造函数中用于获取值,这些值随后将传递给另一个构造函数。当前,它看起来像这样(请注意,代码已缩短为使用2个参数,并删除了不需要的代码):

template<class EditorDialog, typename FirstOpt, typename SecondOpt>
class GenericItemDelegateBase {
public:
    GenericItemDelegateBase( const FirstOpt &first, const SecondOpt &second ) : first( first ), second( second ) {}
protected:
    const FirstOpt first;
    const SecondOpt second;
private:
    EditorDialog *createEditor() const {
        return new EditorDialog( first, second );
    }
};

template<class EditorDialog, typename FirstOpt>
class GenericItemDelegateBase<EditorDialog, FirstOpt, void> {
public:
    GenericItemDelegateBase( const FirstOpt &first ) : first( first ) {}
protected:
    const FirstOpt first;
private:
    EditorDialog *createEditor() const {
        return new EditorDialog( first );
    }
};

template<class EditorDialog>
class GenericItemDelegateBase<EditorDialog, void, void> {
public:
    GenericItemDelegateBase() {}
private:
    EditorDialog *createEditor() const {
        return new EditorDialog();
    }
};

template<class EditorDialog, typename FirstOpt = void, typename SecondOpt = void>
class GenericItemDelegate : public GenericItemDelegateBase<EditorDialog, FirstOpt, SecondOpt> {
public:
    using Base = GenericItemDelegateBase<EditorDialog, FirstOpt, SecondOpt>;
    using Base::Base;
};

如您所见,代码中有很多重复项。我能够通过使用继承来删除某些重复项,并拥有一个Base版本,其中包含一些重复代码(在示例中删除)。

我的想法是尝试使用可变参数模板来允许任意数量的参数(我为此选择了3个,到目前为止,该参数一直有效,但是将来可能需要更多参数)。我开始这样实现:

template<class EditorDialog>
class GenericItemDelegate {
    GenericItemDelegate() {}
};

template<class EditorDialog, typename type, typename... args>
class GenericItemDelegate : public GenericItemDelegate<EditorDialog, type, args...> {
    using Base = GenericItemDelegate<EditorDialog, type, args...>;
    GenericItemDelegate( const type &var, args &&... rest ) : Base( std::forward<args>( rest )... ), var( var ) {}
protected:
    const type var;
};

我遇到的问题是此版本,我不知道如何实现createEditor函数。它必须将所有变量传递给EditorDialog构造函数,我不确定该怎么做(或者甚至可以做到)。

QWidget *createEditor() const {
    return new EditorDialog( [all vars...] );
}

我不知道这对于我设置的继承结构是可行的,因为根据所使用的EditorDialog,它可能具有一个构造函数,该构造函数接受三个参数,但没有一个仅接受两个参数。

我是否完全走错了路?还是有可能使这项工作可行?

用法示例:

int main() {
    auto a = GenericItemDelegate<int>();
    auto b = GenericItemDelegate<int, int>( 3 );
    auto c = GenericItemDelegate<std::vector<int>, int>( 3 );
    auto d = GenericItemDelegate<std::vector<int>, int, std::allocator<int>>( 3, std::allocator<int>() );
}
c++ templates variadic-templates
3个回答
1
投票

您可以使用std :: tuple存储Opts包,然后传递std::index_sequence以使其可以通过std::get进行检索。

类似的东西

template<class...>
class GenericItemDelegateBase_impl;

template<class EditorDialog, std::size_t... Is, class... Opts>
class GenericItemDelegateBase_impl<EditorDialog, std::index_sequence<Is...>, Opts...> : public QItemDelegate {
public:
    GenericItemDelegateBase_impl( QSqlDatabase &connection, QObject *parent, Opts... opts ) : QItemDelegate( parent ), connection( connection ), m_opts(std::move(opts)...) {}
protected:
    QSqlDatabase connection;
    std::tuple<Opts...> m_opts;
private:
    QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const override {
        return new EditorDialog( connection, parent, std::get<Is>(m_opts)...);
    }
};

template <class EditorDialog, class... Opts>
using GenericItemDelegateBase = GenericItemDelegateBase_impl<EditorDialog, std::make_index_sequence<sizeof...(Opts)>, Opts...>;

由于这里有很多Qt类型,所以我没有尝试编译它,但是从一些可能的错别字或较小的错误中就可以了。

编辑如注释中所建议,使用std::apply和lambda,我们可以进一步简化代码。这需要c++14才能使用通用lambda(自动参数)。

#include <tuple>

template<class EditorDialog, class... Opts>
class GenericItemDelegateBase : public QItemDelegate {
public:
    GenericItemDelegateBase( QSqlDatabase &connection, QObject *parent, Opts... opts ) : QItemDelegate( parent ), connection( connection ), m_opts(std::move(opts)...) {}
protected:
    QSqlDatabase connection;
    std::tuple<Opts...> m_opts;
private:
    QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &, const QModelIndex & ) const override {
        return std::apply([&](auto&&... opts) { return new EditorDialog(connection, parent, opts...); });
    }
};

1
投票

不同的接口和类型擦除如何?

template<class EditorDialog>
class GenericItemDelegate
{
    std::function<std::unique_ptr<EditorDialog>()> mFactory;
public:
    template <typename ... Ts>
    GenericItemDelegateBase(Ts... args) :
        mFactory([=](){ return std::make_unique<EditorDialog>(args...); })
    {}
//private:
    EditorDialog* createEditor() const { return mFactory().release(); }
};

然后将其用作:

GenericItemDelegate<int> a;
GenericItemDelegate<int> b(3);
GenericItemDelegate<std::vector<int>> c(3);
GenericItemDelegate<std::vector<int>> d(3, std::allocator<int>());

1
投票

您可以将参数存储到std::tuple,然后像以下那样将其传递到EditorDialog

template<class EditorDialog, typename ... Args>
class GenericItemDelegateBase {
public:
    GenericItemDelegateBase(Args&&... args)
        : args(std::forward<Args>(args)...)
    {}

protected:
    std::tuple<Args...> args;

private:
    EditorDialog createEditor() const {
        return std::make_from_tuple<EditorDialog>(args);
    }
};

然后下面将完成工作:

auto a = GenericItemDelegateBase<int>();
auto b = GenericItemDelegateBase<int, int>( 3 );
auto c = GenericItemDelegateBase<std::vector<int>, int>( 3 );
auto d = GenericItemDelegateBase<std::vector<int>, int, std::allocator<int>>( 3, std::allocator<int>() );

请注意,std::make_from_tuple的使用需要C ++ 17

© www.soinside.com 2019 - 2024. All rights reserved.