我想使用一个地图来引用一个类型说明符,主要是为了缩短我的代码来多次使用
std::unique_ptr< Class >(new class1);
到
std::unique_ptr< Class >(new sampleMap[enum1]);
然后定义我的地图,以便它将每个枚举值(enum1
,enum2
,...)引用到我的班级(class1
,class2
,.. )。
但是我无法定义我的地图,其值是类似名称
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]);
加上地图声明。
有任何想法可以做到这一点吗?
答案 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
或其他效率较低的技巧,但编译器完全优化它会更加困难(我没有找到它)。
请注意,在GCC,Clang,Visual C++和the Intel compiler之外,只有Clang能够使用此Switch
结构完全优化主函数。 GCC和Visual C ++能够优化它以调用Class2
的{{1}}。英特尔编译器似乎根本没有优化它(但也许我不知道它的正确标志)