具有特定类型但没有模板

时间:2016-07-20 14:14:22

标签: c++ templates data-structures

我注意到大多数(全部?)可以存储用户定义类型的C ++数据结构使用模板:

std::vector<T>, std::unordered_map<T....>, etc.

在我看来,它是必需的,否则这些数据结构将必须作为用户必须手动投射的险恶指针void*返回,如下所示:

std::vector a; 
int b;
a.push_back(b);
static_cast<int>(a.get(0));

是否可以不使用模板?还有其他替代方法吗?

修改 许多评论表明,没有模板,这是不可能的/不切实际的。 (谢谢!)

如果我将<T>限制为真正的指针(例如,不是int / float),那么它仍然不切实际吗?

4 个答案:

答案 0 :(得分:4)

  

是否可以不使用模板?

是。可以定义一般数据结构,允许在不使用模板的情况下对&lt; - 我不知道在何处使用任何类型的动词对象进行分组。

有&#34; C方式&#34; (C中不存在模板)。 C方式是使用void*。您显然已经熟悉了,但对于答案的其他读者,void*是一种特殊的指针类型,可以指向任何类型的对象。当您使用void*来引用某个对象时,您基本上会抛弃该语言提供的所有类型安全性。

我不认为标准库中有任何非模板容器。但是,如果使用提供的模板实例化std::vector<void*>或类似的并不是太多,那么您当然可以使用此类模板实例来存储指向任何对象的指针。

模板非常优先于void*,因为模板不会将类型安全性抛出窗口。模板更容易正确使用。

还没有。但是计划在C ++ 17中引入std::any。它可以代替void*使用。使用std::any你仍然会抛弃编译时类型的安全性,但至少你保留了运行时的安全性(当你有bug时,你会得到一个异常,而不是潜在的龙)。与void*不同,std::any管理存储对象的内存。请注意,虽然std::any不是模板,但其大多数成员函数都是。

  

还有其他替代方法吗?

除了模板和void*?从技术上讲,您也可以使用宏来生成相同程序的不同版本,类似于实例化模板。这有时在C中使用。在C ++中没有理由这样做,因为模板在各方面都更好。

答案 1 :(得分:2)

我将举例说明为什么使用这样的集合会有危险。请考虑以下代码:

#include <vector>
#include <iostream>

using anyvector = std::vector<void *>;

int main() {
   anyvector av;
   av.push_back(new int(10));
   std::cout << (*static_cast<int*>(av[0])) << std::endl;
}

av[0]是指向使用new关键字的老式c ++方式创建的int的指针。因此,应该使用delete关键字释放以这种方式分配的内存。但要做到这一点,应首先将指针强制转换为正确的类型,让编译器知道他真正删除了什么,以防止出现未定义的行为。

要明确我并不是说这是不可能的也不是不切实际的,但它要求程序员全意识......

答案 2 :(得分:2)

您可以依赖类型擦除结构 作为一个极小的,有效的和天真的例子:

#include<memory>
#include<iostream>
#include<vector>

struct Container {
    struct B {
        virtual void operator()() = 0;
    };

    template<class T>
    struct D: B {
        D(T t): o{t} {}
        void operator()() override { o(); }
        T o;
    };

    template<typename T>
    void add(T t) {
        vec.push_back(std::unique_ptr<B>{new D<T>{t}});
    }

    void operator()() {
        for(auto &ref: vec) (*ref)();
    }

    std::vector<std::unique_ptr<B>> vec;
};

struct P {
    void operator()() { std::cout << "P" << std::endl; }
};

struct Q {
    void operator()() { std::cout << "Q" << std::endl; }
};

int main() {
    Container c;
    P p;
    Q q;
    c.add(p);
    c.add(q);
    c();
}

C ++ 17可能会引入std::any并让您轻松完成 在此之前,您可以使用boost::any或自制类型的擦除类,如上所述。

答案 3 :(得分:2)

我同意当你不能使用容器时,模板是接近容器的正确方法,std::any(或boost::any)。然而,仅仅为了有趣的部分,我想指出,通过例外可以实现完美的类型重建。请注意,非常推荐,我只是为了完整性而添加它。在这种情况下,

  • 你的矢量就像std::vector<std::function<void()>>(可以简化),
  • 您推送v.push_back([e](){ throw e; });
  • 等元素
  • 你访问的元素,不仅仅是catch例如由{1}抛出的异常。 try{ v[i](); } catch(EType& e) { ... }

这很难看,不要这样做,这很慢,不要这样做..但是,另一方面,它允许完美的访问(包括继承,你没有得到{{1开放层次结构(你不会用type_info获得),链接访问者,而不需要基类和&amp;其他虚拟功能。

大胖警告:你可能不会通过这样的代码传递我的CR - 它只是为了有趣和理论上的完整而被提及。