Ich habe eine Klasse Foo
, die verschiedene Prädikatvarianten über seinen Konstruktor akzeptiert.Übergabe verschiedener Lambdas zu Funktionsvorlage in C++
template<typename T>
struct Value
{
T value;
};
class Foo
{
public:
template<typename T>
Foo(Value<T> &value, function<bool()> predicate)
{
}
template<typename T>
Foo(Value<T> &value, function<bool(const Value<T> &)> predicate) :
Foo(value, function<bool()>([&value, predicate](){ return predicate(value); }))
{
}
};
Dies ermöglicht es mir, die Klasse mit explizitem function
Objekt zu konstruieren:
Value<int> i;
Foo foo0(i, function<bool()>([]() { return true; }));
Foo foo1(i, function<bool(const Value<int> &)>([](const auto &) { return true; }));
jedoch scheitert es, wenn sie direkt ein Lambda zu verwenden versuchen:
Foo fooL1(i, [](const Value<int> &) { return true; });
Aus einem Grunde, I don‘ Ich verstehe, aber der Compiler berücksichtigt nicht die Verfügbarkeit der impliziten Konvertierung von Lambda zu function
in Konstruktor Vorlage. Die Fehlermeldung ist (Visual C++ 2015, Update 3):
Fehler C2664: 'Foo :: Foo (Foo & &)': Konvertierung Argument 2 von 'main :: < lambda_f1d2143f356d549800fb3412d8bc61a2>' zu 'std :: function < bool (void)>'
Jetzt kann ich anderen Konstruktor Vorlage für lambdas hinzufügen
template<typename T, typename UnaryPredicate>
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<bool(const Value<T> &)>(predicate))
{
}
, die in Ordnung, solange das Lambda zu diesem Konstruktor übergibt funktioniert hat einen Parameter von Value<T>
, aber es ist nicht natürlich für lambdas ohne Parameter:
Foo fooL0(i, []() { return true; });
Also ich würde wahrscheinlich einige SFINAE Magie benötigen entsprechende Konstruktor Vorlage zu ermöglichen, für verschiedene lambdas, so etwas wie:
template<typename T, typename UnaryPredicate,
typename = enable_if_t<is_callable_without_args> >
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<bool()>(predicate))
{
}
template<typename T, typename UnaryPredicate,
typename = enable_if_t<is_callable_with_one_arg> >
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<bool(const Value<T> &)>(predicate))
{
}
Oder vielleicht nur ein Konstruktor Vorlage könnte den Trick tun, so etwas wie:
template<typename T, typename UnaryPredicate>
Foo(Value<T> &value, UnaryPredicate predicate) :
Foo(value, function<???decltype(UnaryPredicate)???>(predicate))
{
}
Oder vielleicht eine ganz andere Lösung? Die Frage ist, wie Konstruktorüberladungen mit entsprechenden Lambdas arbeiten können.
Großartig, obwohl ich nicht hinter der Magie sehe, funktioniert es wie ein Zauber. Können Sie mir irgendwann auf einige Informationen hinweisen, die diese Technik in lesbarer Form beschreiben? Vielen Dank. – manison
@manisin Block deduceton nimmt einen Typ und gibt einen gleichen Typ zurück. Der zurückgegebene Typ ist jedoch nicht mehr ableitbar. Die Vorlaßtypableitung ist ein Mustervergleich, und für 'tag_t :: type' kann kein Musterabgleich durchgeführt werden. Kurz gesagt, weil der Standard das sagt. Warum sagt es so? In der Theorie könnte ":: type" nicht mit "T" in Beziehung stehen, in Wirklichkeit ist es "T". C++ benötigt das Typabzugssystem nicht, um beliebige Turing-vollständige Prozesse zu invertieren, und "tag_t :: type" könnte eine beliebige Zuordnung von "T" zu ":: type" haben. –
Yakk