Zażądano konwersji z lambda na typ nieskalarny
Utworzyłem tę klasę, aby móc mieć wartość dowolnego typu, która jest stała lub ponownie obliczana za każdym razem, gdy jest używana:
template<typename T>
class Value {
private:
bool fixed;
union {
T value;
std::function<T()> get;
};
public:
Value(const T& value) : fixed(true), value(value) {}
Value(const std::function<T()>& get) : fixed(false), get(get) {}
Value(const T *pointer) : Value([pointer]() { return *pointer; }) {}
~Value() {}
operator T() { return fixed ? value : get(); }
};
Wszystkie poniższe wyrażenia wydają się działać dobrze:
Value<double> a = 2.2;
double b = 1.;
double c = a;
Value<double> d = &b;
Value<int> e = Value<int>([]() { return 1.; });
Ale kiedy próbuję zrobić:
Value<double> f = []() { return 1.; };
wyzwalany jest błąd kompilacji:
error: conversion from 'main()::<lambda()>' to non-scalar type 'Value<double>' requested
Możesz wypróbować ten przykład tutaj .
Dlaczego przypisywanie działa dla T
i nie std::function<T()>
i jak mogę to zrobić?
Uwaga: jestem świadomy tej odpowiedzi, ale nie zrozumiałem, jak rozwiązać problem bez konieczności jawnego wywoływania konstruktora, tak jak to zrobiłem Value<double> e
.
Odpowiedzi
Dlaczego przypisywanie działa dla T, a nie std :: function <T ()> i jak mogę to zrobić?
Twój kod nie używa przypisania, ale kopiuje inicjalizację i
Ponadto niejawna konwersja w inicjalizacji kopiowania musi generować T bezpośrednio z inicjatora, podczas gdy np. Inicjalizacja bezpośrednia oczekuje niejawnej konwersji z inicjatora na argument konstruktora T.
Aby to zadziałało, musisz sprawić, aby Twój ctor bezpośrednio akceptował lambdę (to jest uproszczony przykład):
template<typename T>
class Value {
std::function<T()> get;
public:
template<class Y>
Value(Y lambda ) : get( std::move( lambda ) ) {}
};
kod na żywo Prawdopodobnie chcesz dodać ograniczenie za pomocą std::enable_if
koncepcji lub, jeśli C ++ 20 jest dozwolone dla tego ctora, a także w tej formie ten konstruktor próbowałby zaakceptować wszelkie inne przeciążenia, które by nie powodowały i mogą powodować tajemnicze błędy. Zgodnie z tym parametrem szablonu enable_if jest lambda (z określonym podpisem) , może to być tak proste, jak
template<class Y, typename = decltype(std::declval<Y&>()())>
Value(Y lambda ) : get( std::move( lambda ) ) {}
który obsługuje C ++ 14. Oto kolejny przykład na żywo, w którym widać, że ten konstruktor nie jest używany do inicjowania typu int
:
Value<double> d2 = 123;
prog.cpp: 9: 5: uwaga: szablon kandydujący zignorowany: błąd podstawienia [z Y = int]: wywołany typ obiektu 'int' nie jest funkcją ani wskaźnikiem funkcji Wartość (Y lambda): get (std :: move (lambda )) {}
Lambda nie jest std::function
. To znaczy, kiedy to robisz
Value<double> f = []() { return 1.; };
musisz przekonwertować []() { return 1.; }
na konwersję std::function
, która jest konwersją zdefiniowaną przez użytkownika, a następnie musisz ją przekonwertować std::function
na Value<double>
inną konwersję zdefiniowaną przez użytkownika. To dwie konwersje zdefiniowane przez użytkownika, kiedy możesz tylko jedną taką konwersję. Dlatego kod nie może się skompilować.