【问题标题】:C++ Type Erasure for function parameters函数参数的 C++ 类型擦除
【发布时间】:2021-02-16 20:12:15
【问题描述】:

所以在 Java 中我们有泛型,因此我希望在 C++ 中实现类似于以下的东西

public interface ListenerIF <T> {
    public void onChange(T eventData);
}

...

public static void main(String[] args) {
    List<ListenerIF<String>> foo = ...;
    List<ListenerIF<Integer>> bar = ...;
    foo.add((strUpdate) -> {/*some string operation*/});
    bar.add((intUpdate) -> {/*some math*/});
    foo.forEach((listener) -> listener.onChange("some change"));
    bar.forEach((listener) -> listener.onChange(123));
}

但是由于 C++ 模板根本不同(并且需要预先实现所有实现),我试图了解如何使用类型擦除在 C++ 中完成类似的事情(并且由于 JVM 是用 C++ 编写的,我很确定这是可能的,只是躲避我)。

我可以根据ListenerIF 在我想要的地方进行类型擦除,但我不知道如何为函数ListenerIF::onChange(T) 使类型T 动态化。这就是我让 ListenerIF 工作的结果(没有 T 是动态的,这里就像 std::string 一样):

class ListenerIF {
  public:
    //how do I get the parameter to this function to be dynamic?
    virtual void onChange(std::string) = 0;
};

template<typename LISTENER>
class Listener: public ListenerIF {
  public:
    //how do I get the parameter to this function to be dynamic?
    void onChange(std::string update) {
      l.onChange(update);
  private:
    LISTENER l;
};

class Foo {
  public:
    //how do I get the parameter to this function to be dynamic?
    void onChange(std::string);
};
class Bar {
  public:
    //how do I get the parameter to this function to be dynamic?
    void onChange(std::string);
};

void Foo::onChange(std::string update){}
void Bar::onChange(std::string update){}

int main() {
  std::vector<ListenerIF *> listeners;
  listeners.push_back(new Listener<Foo>());
  listeners.push_back(new Listener<Bar>());
  for(std::vector<ListenerIF *>::iterator listenersItr = listeners.begin(); listenersItr < listeners.end(); listenersItr++){
    (*listenersItr)->onChange("some string update");
}

如果我只想将 'onChange' 用于字符串,这一切都很好,但如果我想将一个完全不同的 'onChange' 类型作为 int、float 或其他一些对象类型,那么这就崩溃了.

我只是不理解一些我遗漏的简单内容吗?

【问题讨论】:

  • 旁注:写listenersItr == listeners.end(),而不是listenersItr &lt; listeners.end()(迭代器==可比较,但不一定++listenersItr,而不是listenersItr++,这个不需要临时的。
  • 这更像是伪代码而不是代码。我正在从另一个未接入互联网的工作站转录。
  • 您已经在此处发布了正确的关键字作为标签:type-erasure。假设您使用指针(与 java 引用非常相似),您可以使用每个指针类型都可以转换为的类型:void*,这将允许您执行 virtual void onChange(void*) = 0;void Foo::onChange(void* update){ const std::string* str = dynamic_cast&lt;const std::string*&gt;(update); if (str == nullptr) throw ClassCastException; ... delete str;/* not sure this is the correct place to free the memory */ },但我会考虑质量这段代码相当低......

标签: c++ type-erasure


【解决方案1】:

Java版本中没有删除参数(ListenerIF&lt;T&gt;)。

等效的 C++ 也会保留参数:

template <typename T> // <- need a template parameter
class listener_interface {
public:
    virtual void on_change(const T& data) = 0;
};

【讨论】:

    【解决方案2】:

    您需要将模板应用于ListenerIF 本身,就像Java 代码一样,例如:

    #include <vector>
    #include <memory>
    
    template <typename T>
    class ListenerIF {
    public:
        virtual ~ListenerIF() {};
        virtual void onChange(const T &eventData) = 0;
    };
    
    template <typename T>
    using listenerIF_ptr = std::unique_ptr<ListenerIF<T>>;
    
    ...
    
    class FooListener : public ListenerIF<std::string> {
    public:
        void onChange(const std::string &eventData) override {
            /* some string operation */
        }
    };
    
    class BarListener : public ListenerIF<int> {
    public:
        void onChange(const int &eventData) override {
            /* some math */
        }
    };
    
    ...
    
    int main() {
        std::vector<ListenerIF_ptr<std::string>> foo;
        std::vector<ListenerIF_ptr<int>> bar;
    
        foo.push_back(std::make_unique<FooListener>());
        bar.push_back(std::make_unique<BarListener>());
    
        for(auto &listener : foo) { listener->onChange("some change"); }
        for(auto &listener : bar) { listener->onChange(123); }
    }
    

    Demo

    但是,在这种情况下,我建议使用std::function(甚至是普通函数指针)和lambdas,而不是使用多态接口类型,例如:

    #include <vector>
    #include <functional>
    
    template <typename T>
    using listenerIF = std::function<void(const T&)>;
    // or: listenerIF = void(*)(const T&);
    
    int main() {
        std::vector<listenerIF<std::string>> foo;
        std::vector<listenerIF<int>> bar;
    
        foo.push_back(
            [](const std::string &eventData){
                /* some string operation */
            }
        );
        bar.push_back(
            [](const int &eventData) {
                /* some math */
            }
        );
    
        for(auto &listener : foo) { listener("some change"); }
        for(auto &listener : bar) { listener(123); }
    }
    

    Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多