【问题标题】:Reinterpret cast void without type identifier重新解释没有类型标识符的 cast void
【发布时间】:2016-12-12 11:12:14
【问题描述】:

我正在使用类型擦除(void cast)将多种基本类型(float、bool、int 等)存储在一对一的映射中。重新解释原始数据类型的一种方法是使用对/联合/类来存储具有类型标识符(例如对)的值。有没有一种干净的方法来使用没有 Boost 的 C++11 来解析没有标识符的基本类型?

std::map<int, void *> m_mymap;
// avoid std::map<int, pair<void, MyEnum> > m_mymap;

template <class T> void setValue<T>(int i_key, T i_val)
{
    m_mymap[i_key] = reinterprete_cast<void *>(i_val);
}

template <class T> T getValue<T>(int i_key)
{
    return reinterprete_cast<T>(i_val);
}

doSomeWork()
{
    for (const auto & elem : m_mymap)
    {
        auto val = some_magical_cast< ??? >(elem.second) // resolve type without a nasty switch against MyEnum 
        // do some work
    }
}

编辑:更新问题以使用 void *

【问题讨论】:

  • void 不包含您想要的任何类型的数据 - 它什么都不包含。
  • 基本上 void * cast 类似于使用 map 的想法
  • 有什么理由不想使用boost::any?看起来它正是这项工作的工具。
  • 它更多的是对使用 Boost 的限制——我以前使用 boost::any 来解决这个问题。当然 C++17 中的 std::any 也不是一个选项
  • 是的,对不起,我来得太晚了,哈哈

标签: c++ c++11 templates casting type-erasure


【解决方案1】:

正如@NicolBolas 正确解释的那样,你不能。

作为一种可行的方法/解决方法,您可以使用基于双重调度思想的技术,如下例所示:

#include<map>
#include<utility>
#include<iostream>

class C {
public:
    template<typename T>
    void accept(int k, T v) { std::cout << k << " " << v << std::endl; }
};

using P = void(*)(C &, int, void *);

std::map<int, std::pair<P, void*>> m;

template<typename T>
void proto(C &c, int k, void *v) {
    c.accept(k, *static_cast<T*>(v));
}

template <class T>
void set(int k, T *v) {
    m[k] = { &proto<T>, v };
}

void get(C &c, int k) {
    auto p = m[k];
    p.first(c, k, p.second);
}

int main() {
    int i = 42;
    double d = 1.2;

    set(0, &i);
    set(1, &d);

    C c;
    for(auto &&k: m) {
        get(c, k.first);
    }
}

它需要一个额外的类(示例中为C)。
您可以为该类添加针对不同类型的不同方法(例如,您可以使用两种接受intdouble 的方法来代替模板方法)。
通过这种方式,您可以拥有通用的行为以及许多特定于类型的行为。

当然,它更复杂,而且不是免费的,否则你无法获得你想要的东西。

【讨论】:

  • 这项工作现在会进入 c.accept 吗?这里的 get 没有返回一个有效的类型,所以会有很多 get - 1 代表 get,2 代表 void get(int) { c.doWork } 在我看来可读性较差
  • @lukemtesta 当然,这项工作在c.accept 中进行。你不需要更多gets,你需要更多accepts。不幸的是,该解决方案的目标不是可读性,而是能够执行与您想要实现的目标类似的事情。无论如何,双重调度是一种广泛使用的技术,我不会定义它不可读
【解决方案2】:
auto val = some_magical_cast< ??? >(elem.second) // resolve type without a nasty switch against MyEnum 

C++ 是一种静态 类型的语言。在编译时必须知道每种类型。您正在尝试执行的操作需要根据放入地图的内容在运行时确定类型。

这在 C++ 中是不可能的。 val 必须具有单一的特定类型,而不是通过执行运行时代码确定的类型。并且循环的每次迭代都必须赋予它相同的类型。联合或variant 将允许您存储在运行时确定的几种类型之一。但是它们存储的类型集在编译时是固定的。

即使auto val 可能以某种方式导致一系列不同的类型,void* 也不包含任何可用于恢复其指向的类型的信息。请注意,boost/std::any 也缺乏任何恢复这种类型的能力;你必须要求一个特定的类型(不同的是如果你要求错误的类型,any 会失败,而void* 会给你 UB)。

所以不,这是不可能的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-08
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 2013-09-12
    • 1970-01-01
    相关资源
    最近更新 更多