Template in Template Funktion aufrufen, aber mit partieller Typvorgabe
-
Hallo (C++ 23 ...), ich möchte zwei Template Funktionen in einer Test Funktion gegenüberstellen. Der Test Funktion soll nur gesagt werden (via Argument... oder Parametrisierung), welche Template Funktion sie testen soll. Dabei soll die Test Funktion selber festlegen können, wie sie die zu testende Template Funktion parametrisieren will (hier soll nur mit
<int>s getestet werden).#include <cstdint> #include <cmath> #include <chrono> #include <iostream> #include <vector> #include <set> #include <algorithm> #include <numeric> #include <random> using namespace std; template < typename E > void select_n_elements_a(const vector < E > & in, vector < E > & out, const uint32_t n) { static auto gen = default_random_engine { random_device {}() }; vector < uint32_t > indexes(in.size()); iota(begin(indexes), end(indexes), 0); shuffle(indexes.begin(), indexes.end(), gen); sort(indexes.begin(), indexes.begin() + n); for (auto it = indexes.begin(); it != indexes.begin() + n; it++) { out.push_back(in [ * it]); } } template < typename E > void select_n_elements_b(const vector < E > & in, vector < E > & out, const uint32_t n) { static auto gen = default_random_engine { random_device {}() }; ranges::sample(in, back_inserter(out), n, gen); } // test until the first match template < typename F > void test1(F select_n_elements, const uint32_t n, const uint32_t type) { vector < int > in(n); iota(begin(in), end(in), 0); for (int i = 0; i < in.size();) { vector < int > out = {}; select_n_elements(in, out, type == 0 ? 1 : n - 1); if (type == 0 && out[0] == i || type == 1 && find(out.begin(), out.end(), i) == out.end()) { i++; } } } int main() { const uint32_t n = 100; auto start = chrono::system_clock::now(); test1(select_n_elements_a < int >, n, 0); auto end = chrono::system_clock::now(); chrono::duration < double > elapsed = end - start; cout << "select_n_elements_a 1. n=" << n << ": " << elapsed.count() << endl; start = chrono::system_clock::now(); test1(select_n_elements_a < int >, n, 1); end = chrono::system_clock::now(); elapsed = end - start; cout << "select_n_elements_a 2. n=" << n << ": " << elapsed.count() << endl; start = chrono::system_clock::now(); test1(select_n_elements_b < int >, n, 0); end = chrono::system_clock::now(); elapsed = end - start; cout << "select_n_elements_b 1. n=" << n << ": " << elapsed.count() << endl; start = chrono::system_clock::now(); test1(select_n_elements_b < int >, n, 1); end = chrono::system_clock::now(); elapsed = end - start; cout << "select_n_elements_b 2. n=" << n << ": " << elapsed.count() << endl; }Das Problem tritt hier in Zeile 57, 63, 69 und 75 auf. Dort muss ich bei
select_n_elements_abzw.select_n_elements_bjedes Malintbei der Initialisierung/Referenz angeben - und das ist nicht gewollt.Wie würde man das richtig machen? Ich glaube, ich habe schon alle SO Fragen dazu gelesen, aber noch auf keinem grünen Zweig... Könnte es sein, dass man beim Aufruf das
templateKeyword zusätzlich verwenden muss?(Bitte nicht zu sehr auf
using namespaceundendl;herumreiten ...)
-
@kali-hi Ich bin kein Experte was die Deduzierungsregeln betrifft. Aber, aus dem Aufruf von
test1lässt sich nicht herleiten, welchen Typ die Parameter von dem Übergebenen Funktionstemplate haben. Das hängt erst an dem Vektor der ja intest1angelegt wird. Aber um test1 zu instantiieren muss der Parametertyp bekannt sein.Du kannst, wenn du nicht jedes mal das
intschreiben willst, ein alias erstellen:auto fkt1 = select_n_elements_a<int>;, oder, wenn das vom Vektor Typ abhängen soll, kannst du den Vektor als Parameter in dietest1mit übergeben und dann sowas machen:std::vector<int> in(n); using ElementType = decltype(in)::value_type; auto fkt1 = select_n_elements_a<ElementType>; test1(fkt1, 0, in); ... template <typename F, typename T> void test1(F select_n_elements<T>, const uint32_t type, std::vector<T>& in)Oder, wenn du deine test1 Funktion so behältst, kannst du dir mit Lambdas helfen:
auto lambdaA = [](const auto& in, auto& out, const uint32_t n) { select_n_elements_a(in, out, n); }; auto lambdaB = [](const auto& in, auto& out, const uint32_t n) { select_n_elements_b(in, out, n); }; test1(lambdA, 0, n);Dann übergibst du nämlich direkt ein callable Objekt.
-
@Schlangenmensch sagte in Template in Template Funktion aufrufen, aber mit partieller Typvorgabe:
std::vector<int> in(n);
using ElementType = decltype(in)::value_type;
auto fkt1 = select_n_elements_a<ElementType>;
test1(fkt1, 0, in);Das ist das Problem, nur verlagert
@Schlangenmensch sagte in Template in Template Funktion aufrufen, aber mit partieller Typvorgabe:
template <typename F, typename T>
void test1(F select_n_elements<T>,
const uint32_t type,
std::vector<T>& in)Ebenfalls das Problem, nur in einer anderen Form
@Schlangenmensch sagte in Template in Template Funktion aufrufen, aber mit partieller Typvorgabe:
auto lambdaA = [](const auto& in,
auto& out,
const uint32_t n) {
select_n_elements_a(in, out, n);
};auto lambdaB = [](const auto& in,
auto& out,
const uint32_t n) {
select_n_elements_b(in, out, n);
};test1(lambdA, 0, n);
Das liest sich spannend... das
intkommt hier im Aufruf und in der Deklaration (bzw. Definition) nicht mehr vor... Ich denke, das könnte es sein.Aber
void test1(F select_n_elements<auto >)
-------------------------------^^^^
(oder etwas ähnliches) wäre nicht möglich, oder?
-
template < typename F > void test1(F select_n_elements, const uint32_t n, const uint32_t type)Was du konzeptuell hier versuchst, ist einen function-pointer zu einer template-function zu formen. Das geht nicht.
Was du aber machen kannst, ist ausselect_n_elements_aundselect_n_elements_bein lambda zu formen.
Das sieht dann so aus: https://godbolt.org/z/sfv5o5hqsAlternativ gäbe es noch andere Varianten aber diese ist mit Abstand die simpelste. Notfalls kannst du diese template Funktionen auch einfach mit einem lambda wrappen.
Wenn es denn unbedingt irgendwas mit dieser Art placeholder sein soll, dann kann dir vll auch ein Hilfs-strukt weiterhelfen: https://godbolt.org/z/Yhh7KvrP9
Wird aber meiner Meinung nach nicht unbedingt besser dadurch.