从int映射到类型说明符

时间:2017-07-10 19:39:38

标签: c++ dictionary

我想使用一个地图来引用一个类型说明符,主要是为了缩短我的代码来多次使用

std::unique_ptr< Class >(new class1);

std::unique_ptr< Class >(new sampleMap[enum1]);

然后定义我的地图,以便它将每个枚举值(enum1enum2,...)引用到我的班级(class1class2,.. )。

但是我无法定义我的地图,其值是类似名称

std::map < int, Class > mapName {
{0, class1},
{0, class1},
...
};

因为地图中不允许使用类型名称。

我正在寻找答案的主要原因是通过更换一系列&#34; if / else if&#34;来使我的代码更简洁。陈述或&#34; switch - 案例&#34;语句只有一行代码,其中输出std::unique_ptr<Class>(new class1);通过我定义的映射动态计算出来。所以,我只输入枚举数并获取为我实例化的相应类。否则,我必须这样做:

if (enum1 = 0)
{
    std::unique_ptr< Class >(new class1);
}
else  if (enum2 = 0)
{
    std::unique_ptr< Class >(new class2);
}

(或开关盒) 但是我想在这一行中做到这一切:

std::unique_ptr<Class>(new sampleMap[enum1]);

加上地图声明。

有任何想法可以做到这一点吗?

3 个答案:

答案 0 :(得分:2)

您无法轻松实现std::map,它会按照您尝试的方式将类型作为值返回。您需要实现自己的类,将类型表示为值。但是,由于您的目标似乎是创建具体类型依赖于值的对象实例,因此一个简单的解决方案是创建函数映射。这假设您要支持的所有类型都来自公共类型。每个值都可以包含构造正确对象的函数。如果您的类型不是从常见类型派生的,那么您需要预先形成进一步的类型擦除(可能使用std::any)。

#include <functional>
#include <iostream>
#include <map>
#include <memory>

// Simple set of classes
// Class is the base type
// Class1 and Class2 derive from Class
struct Class { virtual void func() = 0; };
struct Class1 : Class {
    void func() override { std::cout << "Class1\n"; }
};
struct Class2 : Class {
    void func() override { std::cout << "Class2\n"; }
};

// A map of factory functions
const std::map<int, std::function<std::unique_ptr<Class>()>> mapName = {
    { 1, []() {return std::make_unique<Class1>(); } },
    { 2, []() {return std::make_unique<Class2>(); } }
};

int main()
{
    auto foo = mapName.at(2)(); // Make an object of type associated with the value 2
    foo->func();                // Prints "Class2\n"
    return 0;
}

答案 1 :(得分:1)

根据您要使用此代码的位置,您可能希望使用if-else链执行此操作。 std::function对于编译器来说通常很难进行优化,所以如果你希望这个代码足够频繁地调用,那么编写代码可能会更有效:

(使用@FrançoisAndrieux's example

#include <iostream>
#include <memory>
#include <stdexcept>

// Simple set of classes
// Class is the base type
// Class1 and Class2 derive from Class
struct Class {
    virtual void func() = 0;
};
struct Class1 : Class {
    void func() override { std::cout << "Class1\n"; }
};
struct Class2 : Class {
    void func() override { std::cout << "Class2\n"; }
};

std::unique_ptr<Class> make_class(int i)
{
    if (i == 0) return std::make_unique<Class1>();
    else if (i == 1) return std::make_unique<Class2>();

    throw std::out_of_range{ "Asked to construct an unknown type" };
}

int main()
{
    auto foo = make_class(1); // Make an object of type associated with the value 1
    foo->func(); // Prints "Class2\n"
    return 0;
}

如果值的数量很大,您可以通过二分搜索(或只是switch)获得:

// If there are 128 elements, for example
if (!(0 <= i && i < 128)) throw std::out_of_range{ "..." };

if (i < 64) {
    if (i < 32) {
        ...
    } else {
        ...
    }
} else {
    ...
}

它很乱,但它只在一个地方。

答案 2 :(得分:0)

要制作更优化的版本,您可以执行一些最小的元编程/表达模板:

#include <iostream>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>

// Simple set of classes
// Class is the base type
// Class1 and Class2 derive from Class
struct Class {
    virtual void func() = 0;
};
struct Class1 : Class {
    void func() override { std::cout << "Class1\n"; }
};
struct Class2 : Class {
    void func() override { std::cout << "Class2\n"; }
};

template<typename R, typename SwBase, typename T, typename F>
struct Switch
{
    SwBase base;
    T value;
    F fn;

    constexpr Switch(SwBase base, T value, F fn)
        : base{ std::move(base) }
        , value{ std::move(value) }
        , fn{ std::move(fn) }
    {}

    constexpr R operator()(T val) const {
        if (value == val) return fn();
        return base(val);
    }
};

template<typename R, typename SwBase, typename T, typename F>
constexpr auto make_switch_impl(SwBase&& swb, T&& t, F&& f)
{
    return Switch<R, std::decay_t<SwBase>, std::decay_t<T>, std::decay_t<F>> {
        std::forward<SwBase>(swb),
        std::forward<T>(t),
        std::forward<F>(f),
    };
}

template<typename R>
constexpr auto make_switch(char const* failMsg)
{
    return [=](auto&&) -> R { throw std::out_of_range{ failMsg }; };
}

template<typename R, typename T, typename F, typename... Args>
constexpr auto make_switch(char const* failMsg, T&& val, F&& fn, Args&&... args)
{
    return make_switch_impl<R>(
        make_switch<R>(failMsg, std::forward<Args>(args)...),
        std::forward<T>(val),
        std::forward<F>(fn)
    );
}

auto make_class(int i)
{
    return make_switch<std::unique_ptr<Class>>(
        "Asked to construct an unknown type",
        0, [] { return std::make_unique<Class1>(); },
        1, [] { return std::make_unique<Class2>(); }
    )(i);
}

int main()
{
    auto foo = make_class(1); // Make an object of type associated with the value 1
    foo->func(); // Prints "Class2\n"
    return 0;
}

switch语句将转为:

auto make_class(int i)
{
    return make_switch<std::unique_ptr<Class>>(
        "Asked to construct an unknown type",
        0, [] { return std::make_unique<Class1>(); },
        1, [] { return std::make_unique<Class2>(); }
    )(i);
}

你也可以单独存储“开关”,虽然这使它不太优化(低至与François Andrieux's solution大致相同的水平):

const auto mapName = make_switch<std::unique_ptr<Class>>(
    "Asked to construct an unknown type",
    0, [] { return std::make_unique<Class1>(); },
    1, [] { return std::make_unique<Class2>(); }
);

auto make_class(int i)
{
    return mapName(i);
}

此版本以及原始if-else链,让编译器将make_class函数优化为等效于switch语句。另外,主要功能:

int main()
{
    auto foo = make_class(1); // Make an object of type associated with the value 1
    foo->func(); // Prints "Class2\n"
    return 0;
}

可以优化到相当于:

int main()
{
    std::cout << "Class2\n";
    return 0;
}

虽然存储我提到的std::function或其他效率较低的技巧,但编译器完全优化它会更加困难(我没有找到它)。

请注意,在GCCClangVisual C++the Intel compiler之外,只有Clang能够使用此Switch结构完全优化主函数。 GCC和Visual C ++能够优化它以调用Class2的{​​{1}}。英特尔编译器似乎根本没有优化它(但也许我不知道它的正确标志)