【问题标题】:Template alias of a more generic template using externally defined template type as a template parameter使用外部定义的模板类型作为模板参数的更通用模板的模板别名
【发布时间】:2020-01-17 12:25:02
【问题描述】:

我想采用一个定义良好的模板类,它依赖于 boost::signals2 并将其抽象为更深的通用层,这可以消除依赖关系,允许使用不同的实现,但仍然允许使用更高级别的函数取决于更通用的接口而不是更具体的接口来使用。在我的重构中,我完全被卡住了。而且,我已经知道我在这里有点不合群。今年我才开始真正学习 C++。但是,这有点有趣。但是我对模板的理解缺少一些东西。首先,我正在开发的库将使用 C++17,我使用 clang++ (Mac),并且依赖于 Boost ~1.7.2 Signals2。

考虑以下摘录:

#include <boost/signals2.hpp>

template<typename Functional_T, typename Return_T, typename ...Args_T>
using SignalHandler = typename Functional_T::template Functional_T<Return_T(Args_T...)>;

template<typename Connection_T>
struct SignalConnection; //no issues here, just wraps a private Connection_T with a getter/setter

template<typename Connection_T, typename Functional_T, typename Return_T, typename ...Args_T>
struct ISignalEmitter
{
protected:
    virtual Return_T trigger(Args_T ...args) = 0;
public:
    virtual void disconnectAll() = 0;
    virtual SignalConnection<Connection_T> onSignal(SignalHandler<Functional_T, Return_T, Args_T...> &signalHandler) = 0;
    virtual void cancelOnSignal(const SignalConnection<Connection_T> &connection) = 0;
};

但这就是我完全陷入困境的地方。我想定义的东西,在我的脑海里是这样的:

template<typename Return_T, typename ...Args_T>
using BoostSignals2SignalHandler = SignalHandler<boost::signals2::slot, Return_T, Args_T...>;

而且我想我们都知道会发生什么。 slot 需要模板参数。那么我该如何构建它以使其工作呢?我尝试了各种各样的东西,但我被卡住了。我的下一个猜测是:

template<typename Return_T, typename ...Args_T>
using BoostSignals2SignalHandler = SignalHandler<template boost::signals2::slot::template, Return_T, Args_T...>;

我不想问,但我知道我不能成为第一个试图解开这个谜题的人。并且必须有一种方法以一种易于理解的方式来考虑这些模板,而不仅仅是猜测和测试。我有一个模板别名BoostSignals2SignalHandler,它是SignalHandler 的特定版本减去第一个模板参数,加上使用现有模板作为更通用模板的特定模板参数。

我会问,“这是糟糕的设计吗?”但我觉得这个概念足以解决我想要解决的问题。我只是不明白该怎么做。谁能指出我正确的方向?再说一次,我不想问。搞清楚这个就很头疼了。提前致谢!

【问题讨论】:

  • 所以您希望您的模板别名SignalHandler 将模板作为其第一个参数?你知道template template parameters吗?
  • 我找到了关于模板模板参数的信息,并且我尝试了几个我不明白的咒语,当我把它们敲出来的时候。老实说,这似乎是一种黑魔法资源,而不是我真正了解如何使用的东西。

标签: c++ templates variadic-templates type-alias


【解决方案1】:

我不会将模板参数列为 Return_T, Args_T...,而是按照 Signals2 示例,在 SignalHandler 中使用函数类型的单个参数,例如 Return_T(Args_T...)

然后我们只需将Functional_T 更改为模板模板参数。

template<template <typename> typename Functional_T, typename Signature_T>
using SignalHandler = Functional_T<Signature_T>;

现在传递给SignalHandler 的第一个参数必须是一个模板,可以用1 个模板参数进行实例化。由于您使用的是c++17,因此我们也可以传递具有更多参数的模板,只要它们具有默认值即可。

要在ISignalEmitter 中获得Return_TArgs_T...,我们使用部分特化。

template<typename Connection_T, template <typename> typename Functional_T, typename Signature_T>
struct ISignalEmitter; // Base declaration to match the specialization against

template<typename Connection_T, template <typename> typename Functional_T, typename Return_T, typename ...Args_T>
struct ISignalEmitter<Connection_T, Functional_T, Return_T(Args_T...)> // Matching Return_T and Args_T against Signature_T
{
protected:
    virtual Return_T trigger(Args_T ...args) = 0;
public:
    virtual void disconnectAll() = 0;
    virtual SignalConnection<Connection_T> onSignal(SignalHandler<Functional_T, Return_T(Args_T...)> &signalHandler) = 0;
    virtual void cancelOnSignal(const SignalConnection<Connection_T> &connection) = 0;
};

此时你可以像这样定义BoostSignals2SignalHandler

template<typename Signature_T>
using BoostSignals2SignalHandler = SignalHandler<boost::signals2::slot, Signature_T>;

using MySignalHandler = BoostSignals2SignalHandler<void()>;

【讨论】:

  • 好的,哇,超级棒!你以一种有意义的方式完全解释了这一点。解决方案是正确的。我知道我在哪里搞砸了,即使我还在学习。我正在研究的仅标头库是从我之前一直在研究的代码库中分离出来的;我将概括程序中执行繁重工作的部分,并将其作为开源共享回来。我一定会在源代码中感谢您的贡献。我的结局是:class OO: public ObservableObject&lt;enum OOPropType&gt; { ObservableProperty&lt;int&gt; myValue; }; 感谢您提供模板模式。那行得通。
猜你喜欢
  • 2014-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
相关资源
最近更新 更多