mfro hat geschrieben:
1. warum hat die constexpr-Variante nicht dieselbe (Nicht-) Laufzeit wie die Template-Variante? constexpr sagt doch eigentlich, daß die Geschichte zur Compilezeit auszurechnen ist?
Der Compiler kann nicht durch die Rekursive Funktion schauen. Bei einer Tiefe von 30 ist dass zu aufwendig.
Zudem wird die Funktion in einer Schleife aufgerufen. Der Optimizer gibt daher noch schneller auf.
mfro hat geschrieben:
2. warum werden bei der Template-Variante die Konstruktoren der rekursiven Template-Instanzierungen (einschließlich der Spezialisierungen) anscheinend nicht aufgerufen?
Du erzeugst nur eine Instanz des Typen `Fib_t` in `main()`. Deine Rekursion läuft ja über den Scope `::` und nicht über Instanzen.
Und ich hab mir mal die Mühe gemacht alles mit `constexpr` in C++14 zu implementieren.
Die gesamte Ausgabe wird zur Compile-Zeit generiert, so dass zur Laufzeit nur noch ausgegeben werden muss.
Zusätzlich wird das Programm mit `exit(0)` beendet, damit keine unnötigen Aufräum-Arbeiten die Laufzeit verzögern.
https://godbolt.org/g/XMKcFe
MSVS 2017.4 kann den Code auch kompilieren. Godbolt hat den leider noch nicht.
Code: Alles auswählen
#include <iostream>
namespace {
template<class T, T V>
struct val {
T data{V}; // not needed but VS crashes without this
};
template<char C>
using chr = val<char, C>;
template<size_t S>
using idx = val<size_t, S>;
// constexpr sequence type
template<class T, T... V>
struct seq {
T data[sizeof...(V)]{V...};
};
template<char... C>
using str = seq<char, C...>;
// allow val + val
template<class T, T V, T V2>
constexpr auto operator+(val<T, V>, val<T, V2>) {
return val<T, V + V2>{};
}
// allow str + val
template<class T, T... V, T V2>
constexpr auto operator+(seq<T, V...>, val<T, V2>) {
return seq<T, V..., V2>{};
}
// allow str + str
template<class T, T... V, T... V2>
constexpr auto operator+(seq<T, V...>, seq<T, V2...>) {
return seq<T, V..., V2...>{};
}
// std::to_string is not constexpr yet
template<size_t V>
constexpr static auto to_str(idx<V> = {}) {
if constexpr (V < 10)
return str<'0' + V>();
else
return to_str<V / 10>() + str<'0' + (V % 10)>();
}
// use constexpr template to prevent recursion limit
template<int n>
constexpr auto fib = fib<n - 1> + fib<n - 2>;
template<>
constexpr auto fib<1> = idx<1>{};
template<>
constexpr auto fib<2> = idx<1>{};
// recursive constexpr because the type expands on each iteration
template<size_t N>
constexpr auto gen() {
auto b = to_str<N>() + chr<' '>() + to_str(fib<N>) + chr<'\n'>();
if constexpr (1 == N)
return b;
else
return gen<N - 1>() + b;
}
} // namespace
[[noreturn]] int main() {
constexpr static const auto d = gen<30>(); // ensure all is evaluated at compile time
std::cout.write(d.data, sizeof(d.data));
std::exit(0); // prevent any destructor
}
Selbst mit `gen<100>` sollte die Ausführungszeit kaum messbar sein. Ab `gen<80>` steigt MSVC aus.
Code: Alles auswählen
PS R:\build> (1..500 | % { (Measure-Command {.\ConstexprFib.exe}).TotalMilliseconds } | Measure-Object -Minimum).Minimum
3,7382