Das ist schön und gut für so etwas [](int x){ return x; } als die operator() ist nicht schablonenhaft.
Generische Lambdas erstellen jedoch Vorlagen für die operator() und es ist nur möglich, auf eine konkrete Instanziierung der Vorlage zuzugreifen – was etwas problematisch ist, da ich Vorlagenargumente für die nicht manuell bereitstellen kann operator() da ich nicht weiß, was seine Arität ist.
Also natürlich so etwas wie
auto lambda = [](auto x){ return x; };
auto arity = fInfo<decltype(lambda)>::arity;
funktioniert nicht.
Ich weiß weder, wohin ich umwandeln soll, noch weiß ich, welche Vorlagenargumente ich bereitstellen soll (oder wie viele) (operator()<??>).
Irgendwelche Ideen, wie man das macht?
Der operator() von generischen Lambdas können variadische Vorlagen sein. Was ist Ihre Definition von Arität für diese?
– TC
6. September 14 um 20:13 Uhr
Warum willst du Arity fragen und nicht „Wie viele? ints kannst du akzeptieren?’?
– Yakk – Adam Nevraumont
6. September 14 um 22:42 Uhr
Yakk – Adam Nevraumont
Diese Technik wird in einigen Fällen funktionieren. Ich erstelle eine fake_anything Typ, der fast alles vortäuschen kann, und versuchen Sie, Ihr Lambda mit einigen Instanzen davon aufzurufen.
Beachten Sie, dass ausreichend SFINAE bedeutet, dass das obige Ergebnis ein falsches Ergebnis liefert, ebenso wie die Verwendung von operator., oder Verwendung von operator. auf bestimmte Arten von “abgeleiteten” Typen oder auf Typen basierend auf der fake_anything Parameter usw.
Wenn das Lambda jedoch seinen Rückgabewert mit a angibt ->X Klausel, dann fake_anything ist mehr als gut genug. Der schwierige Teil ist der Umgang mit dem Körper.
Beachten Sie, dass dieser Ansatz oft eine schlechte Idee ist, denn wenn Sie die Stelligkeit einer Funktion wissen möchten, kennen Sie wahrscheinlich auch die Typen der Dinge, mit denen Sie das Funktionsobjekt aufrufen möchten! Und oben beantworte ich diese Frage ganz einfach (kann dieses Funktionsobjekt mit diesen Argumenten aufgerufen werden?). Es kann sogar verbessert werden, zu fragen, “was das längste/kürzeste Präfix dieser Argumente ist, das dieses Funktionsobjekt aufrufen kann”, oder zu behandeln, “wie viele Wiederholungen des Typs X funktionieren, um dieses Funktionsobjekt aufzurufen” (wenn Sie einen sauberen Fehler wünschen, müssen Sie brauchen eine Obergrenze).
scheint nicht zu funktionieren, wenn Lambdas Körper versucht, das zu nutzen auto Parameter in einem Kontext, der nicht von unterstützt wird fake_anything, z.B return x*2; oder return x.get();; weil der Compiler den Körper in auswertet can_invoke<T>
– Piotr Skotnicki
07.09.14 um 09:11 Uhr
@piotrs. ah ja, mit körperbasierter Typableitung muss es sein. Mit -> Es sollte keinen Grund dazu geben, aber das ist immer noch scheiße. Selbst wenn ich jeden Operator überschreibe, gibt es immer noch ..
– Yakk – Adam Nevraumont
07.09.14 um 09:21 Uhr
@piotrs. einige Verbesserungen. Es funktioniert mit x*2, aber nicht x.get() wenn x ist vom Typ auto.
– Yakk – Adam Nevraumont
8. September 14 um 18:17 Uhr
Ich muss beachten, dass VS2015 in den meisten Fällen falsche Ergebnisse liefert. Hier ist die Ausgabe: 0 0 0 -1 2
– Turm120
18. März 16 um 14:24 Uhr
@tower120 Verwenden decltype in MSVC2015 ist ein Rezept für Enttäuschung.
– Yakk – Adam Nevraumont
18. März 16 um 14:37 Uhr
Es ist unmöglich, da der Funktionsaufrufoperator eine variadische Vorlage sein kann. Für Funktionsobjekte im Allgemeinen war dies schon immer unmöglich, und Lambdas in Sonderfällen zu verwenden, weil sie zufällig nicht gleich leistungsfähig waren, war immer eine schlechte Idee. Jetzt ist es nur Zeit für diese schlechte Idee, nach Hause zu kommen, um sich niederzulassen.
Florestan
Dies ist eine c++17-Lösung, die mit generischen und variadischen Lambdas und Funktoren mit variadischem Templatet-Operator() arbeitet. Die Idee ist, den Aufruf rekursiv mit einer absteigenden Anzahl von Argumenten zu simulieren und SFINAE zu verwenden, um die Rekursion zu unterbrechen, wenn die erste übereinstimmende Anzahl von Argumenten gefunden wird. Es wird auf gcc >= 7 und Clang >=5 kompiliert. Ein funktionierendes Beispiel kann gefunden werden Hier.
#include<utility>
constexpr size_t max_arity = 10;
struct variadic_t
{
};
namespace detail
{
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hece, to 'simulate' an arbitrary function signature.
template <size_t>
struct arbitrary_t
{
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T &&();
template <typename T>
operator T &();
};
template <typename F, size_t... Is,
typename U = decltype(std::declval<F>()(arbitrary_t<Is>{}...))>
constexpr auto test_signature(std::index_sequence<Is...>)
{
return std::integral_constant<size_t, sizeof...(Is)>{};
}
template <size_t I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{}))
{
return {};
}
template <size_t I, typename F, typename = std::enable_if_t<(I > 0)>>
constexpr auto arity_impl(...)
{
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, size_t MaxArity = 10>
constexpr auto arity_impl()
{
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity + 1, F>(0);
if constexpr (tmp == MaxArity + 1)
{
// if that works, F is considered variadic
return variadic_t{};
}
else
{
// if not, tmp will be the correct arity of F
return tmp;
}
}
}
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity(F&& f) { return detail::arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity_v = detail::arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, size_t MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_t>;
Verwendung:
auto l = [](auto...){};
static_assert(is_variadic_v<decltype(l)>);
und:
auto l = [](auto, auto, auto){};
static_assert(!is_variadic_v<decltype(l)>);
static_assert(arity(l) == 3);
4 Jahre später haben wir einen Workaround, der nur halb so schlecht ist wie der alte Workaround. Was für ein Fortschritt, C++!
– Scharlachroter Amaranth
24. März 18 um 9:49 Uhr
Dadurch werden die Typen in beliebig_t konvertiert. Wenn Sie also arity_v verwenden möchten, müssen Sie diese in das Lambda zurückwerfen.
– Glühbirne
4. Juli 19 um 20:09 Uhr
Ich würde sagen, das ist teilweise möglich, zumindest kann man das wissen Gesamtarität (auf Vorlagen basierende + reguläre Typen), wenn Sie die Auto-Parameter von explizit instanziieren operator():
template <typename F, typename... Args>
struct autofInfo : fInfo<decltype(&F::template operator()<Args...>)> {};
auto lambda = [](auto x, int y, float z) { return x + y + z; };
auto arity = autofInfo<decltype(lambda), int>::arity;
// ^^^ list of auto parameters instantiations
assert(3 == arity);
Der
operator()
von generischen Lambdas können variadische Vorlagen sein. Was ist Ihre Definition von Arität für diese?– TC
6. September 14 um 20:13 Uhr
Warum willst du Arity fragen und nicht „Wie viele?
int
s kannst du akzeptieren?’?– Yakk – Adam Nevraumont
6. September 14 um 22:42 Uhr