【问题标题】:C++ Higher order templatesC++ 高阶模板
【发布时间】:2020-07-08 11:29:55
【问题描述】:

我正在尝试在 C++ 中创建一个系统,我可以在其中实现许多不同的实体 E,每个实体都具有与之关联的不同类型 T。我想为这些创建一个通用所有者,以便对于与 T1 相关联的 E1、与 T2 相关联的 E2 等实现,我可以将它们包装在一个可以对其进行管理的容器中,例如(在伪代码中)

E<ConcreteT1> e1;
E<ConcreteT2> e2;
//...
Container c;
c.add(e1)
c.add(e2)
//...

我是 C++ 的初学者,我知道基本模板,但我认为我需要某种“高阶模板”,其中顶层 (C) 是通用的,中间层 (E) 是反过来,在随后的级别上是通用的。也许这是一个完全的反模式,我做错了。请让我知道如何解决这个问题。我很乐意使用 C++17 的特性,但如果可能的话,我宁愿避免使用最新最好的 C++20。

谢谢

编辑: 我要解决的实际问题是让接口在某些方法中采用通用 E(事件)。

template <typename E>
class Interface{
  method(E e){...}
}

我想实现许多具体(事件处理程序),每个都有自己的 E(事件种类)

class EOne{}
class ImplOne:Interface<EOne>{
  method(Eone e){...}
}

class ETwo{}
class ImplTwo:Interface<ETwo>{
  method(ETwo e){...}
}

然后在更多的容器中管理它们,比如上面的Container c。然后我将有一个可调用对象队列,其中可调用对象可以使用相应的事件类型调用 methodN。


for(auto &handler:container){
 eventQueue.pop()();
}

我知道我可能走错了路。

【问题讨论】:

  • 这个问题似乎很宽泛或需要更多细节(不清楚您要解决什么问题??)。您是否试图避免继承?变体会有帮助en.cppreference.com/w/cpp/utility/variant 吗?
  • 据我所知,STL 不支持所谓的异构容器,即在您的案例 ConreteT1 和 ConcreteT2 中存储多种输入类型的容器。你想要的容器是什么样的?
  • 还有 std::any 类型 - 类型擦除的基本形式
  • 问题不清楚,但您可能想看看模板模板参数,但很有可能您只是在想一些实际上并不难的东西。你知道some_template&lt;T&gt; 只是一个和其他类型一样的类型,它来自于模板化并不重要?
  • 关于我之前的评论,显然 STL 采用了 @darune 提到的 boost 的“任何”

标签: c++


【解决方案1】:

您可以使用std::variant 并使用访问。

例如。队列对象可以定义为

using InterfaceVar = std::variant<Interface<E1>, Interface<E2>>;
std::queue< InterfaceVar > eventQueue;

然后您将使用访问来区分类型(听起来您想要这样)。您可以使用std::visit 进行此访问或您自己的访问方式。

Example from cppreference:

#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
 
// the variant to visit
using var_t = std::variant<int, long, double, std::string>;
 
// helper constant for the visitor #3
template<class> inline constexpr bool always_false_v = false;
 
// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
 
int main() {
    std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
    for(auto& v: vec) {
 
        // 1. void visitor, only called for side-effects (here, for I/O)
        std::visit([](auto&& arg){std::cout << arg;}, v);
 
        // 2. value-returning visitor, demonstrates the idiom of returning another variant
        var_t w = std::visit([](auto&& arg) -> var_t {return arg + arg;}, v);
 
        // 3. type-matching visitor: a lambda that handles each type differently
        std::cout << ". After doubling, variant holds ";
        std::visit([](auto&& arg) {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, int>)
                std::cout << "int with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, long>)
                std::cout << "long with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, double>)
                std::cout << "double with value " << arg << '\n';
            else if constexpr (std::is_same_v<T, std::string>)
                std::cout << "std::string with value " << std::quoted(arg) << '\n';
            else 
                static_assert(always_false_v<T>, "non-exhaustive visitor!");
        }, w);
    }
 
    for (auto& v: vec) {
        // 4. another type-matching visitor: a class with 3 overloaded operator()'s
        std::visit(overloaded {
            [](auto arg) { std::cout << arg << ' '; },
            [](double arg) { std::cout << std::fixed << arg << ' '; },
            [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
        }, v);
    }
}

【讨论】:

    猜你喜欢
    • 2014-10-23
    • 1970-01-01
    • 2021-04-09
    • 2019-04-20
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 2014-07-23
    • 1970-01-01
    相关资源
    最近更新 更多