Um eine optimierbar Version zu machen, können Sie einige minimale metaprogramming/Ausdrucksvorlagen tun:
#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;
}
Die switch-Anweisung in das verwandeln würde:
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);
}
Sie auch konnte Speichern Sie den "Schalter" separat, obwohl dies die Option weniger optimierbar macht (bis auf ungefähr das gleiche Niveau wie 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);
}
Diese Version, und auch roh if-else-Ketten, lassen Sie den Compiler die make_class
Funktion auf das Äquivalent einer switch
Anweisung optimieren. Auch die Hauptfunktion:
int main()
{
auto foo = make_class(1); // Make an object of type associated with the value 1
foo->func(); // Prints "Class2\n"
return 0;
}
Kann auf das Äquivalent optimiert werden:
int main()
{
std::cout << "Class2\n";
return 0;
}
Die Speicherung der std::function
oder die anderen weniger effizient Tricks, die ich erwähnt habe, macht es viel schwieriger für den Compiler um es vollständig zu optimieren (ich habe keinen gefunden, der das tut).
Beachten Sie, dass aus GCC, Clang, Visual C++ und the Intel compiler, nur Clang konnte vollständig die Hauptfunktion mit dieser Switch
Struktur optimieren. GCC und Visual C++ konnten es zu einem Anruf Class2
func()
optimieren. Der Intel-Compiler scheint nicht es überhaupt optimiert haben (aber vielleicht weiß ich nicht die richtigen Flaggen für sie)
Was wollen Sie eigentlich zu tun? Dies scheint wie ein [XY Problem] (https://meta.stackexchange.com/q/66377/218012) – Justin
Es ist möglich, mit Vorlagen, um 'neuen type_map :: Arbeit Baumuster zur, und dann können Sie Vorlage verwenden Rekursion zum Durchlaufen einer Anzahl von Werten. Aber ich stimme Justin zu, sag uns, was du erreichen willst, es könnte einen noch besseren Weg geben. –
Es hört sich so an, als könnte dies ein Kandidat für ein Template-Type-Parameter-Pack sein. –