Wiem, że następujący kod nie zostanie skompilowany.
void baz(int i) { }
void baz() { }
class Bar
{
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = baz) : bazFn(fun){}
};
int main(int argc, char **argv)
{
Bar b;
return 0;
}
Ponieważ std::function
mówi się, że nie należy rozważać rozdzielczości przeciążenia, jak czytałem w tym innym poście .
Nie do końca rozumiem ograniczenia techniczne, które wymusiły tego rodzaju rozwiązanie.
Czytałem o fazach tłumaczenia i szablonach na temat preferencji, ale nie mogę wymyślić żadnego uzasadnienia, na które nie mogłem znaleźć kontrprzykładu. Wyjaśniony pół-laikowi (wciąż nowicjuszowi w C ++), co i na jakim etapie tłumaczenia sprawia, że powyższe się nie kompiluje?
c++
templates
translation
std-function
TuRtoise
źródło
źródło
Odpowiedzi:
To tak naprawdę nie ma nic wspólnego z „fazami tłumaczenia”. Chodzi wyłącznie o konstruktorów
std::function
.Zobacz,
std::function<R(Args)>
nie wymaga, aby dana funkcja była dokładnie tego samego typuR(Args)
. W szczególności nie wymaga, aby otrzymał wskaźnik funkcji. Może przyjmować dowolny możliwy do wywołania typ (wskaźnik funkcji składowej, jakiś obiekt, który ma przeciążenieoperator()
), pod warunkiem, że można go wywoływać tak, jakby pobierałArgs
parametry i zwraca coś, co można przekonwertowaćR
(lub jeśliR
jestvoid
, może zwrócić cokolwiek).Aby to zrobić, odpowiedni konstruktor
std::function
musi być szablon :template<typename F> function(F f);
. Oznacza to, że może przyjąć dowolny typ funkcji (z zastrzeżeniem powyższych ograniczeń).Wyrażenie
baz
reprezentuje zestaw przeciążenia. Jeśli użyjesz tego wyrażenia do wywołania zestawu przeciążenia, nie ma sprawy. Jeśli użyjesz tego wyrażenia jako parametru dla funkcji, która pobiera określony wskaźnik funkcji, C ++ może zmniejszyć zestaw przeciążeń do pojedynczego wywołania, dzięki czemu będzie dobrze.Jednak gdy funkcja jest szablonem i używasz dedukcji argumentów szablonu, aby dowiedzieć się, co to za parametr, C ++ nie ma już możliwości ustalenia, jakie jest prawidłowe przeciążenie w zestawie przeciążenia. Musisz więc podać to bezpośrednio.
źródło
function
szablonie klasy nie ma znaczenia . Liczy się to parametr szablonu na konstruktora dzwonisz. Co jest po prostutypename F
: aka, dowolny typ.Rozwiązanie problemu przeciążenia występuje tylko wtedy, gdy (a) wywołujesz nazwę funkcji / operatora lub (b) przesyłasz ją do wskaźnika (do funkcji lub funkcji członka) z wyraźną sygnaturą.
Tutaj też nie ma miejsca.
std::function
pobiera dowolny obiekt zgodny z jego podpisem. Nie bierze specjalnie wskaźnika funkcji. (lambda nie jest funkcją std, a funkcja std nie jest lambda)Teraz w moich wariantach funkcji homebrew, do podpisu
R(Args...)
akceptuję równieżR(*)(Args...)
argument (dokładne dopasowanie) właśnie z tego powodu. Ale oznacza to, że podnosi podpisy „ścisłe dopasowanie” ponad „zgodne” podpisy.Podstawowym problemem jest to, że zestaw przeciążeń nie jest obiektem C ++. Możesz nazwać zestaw przeciążenia, ale nie możesz go przekazać „natywnie”.
Teraz możesz utworzyć zestaw pseudo-przeciążenia funkcji takiej jak ta:
tworzy to pojedynczy obiekt C ++, który może rozwiązać problem przeciążenia dla nazwy funkcji.
Rozszerzając makra, otrzymujemy:
irytujące do pisania. Prostsza, tylko nieco mniej użyteczna wersja jest tutaj:
mamy lambda, która przyjmuje dowolną liczbę argumentów, a następnie doskonale je przekazuje
baz
.Następnie:
Pracuje. Odraczamy rozdzielczość przeciążenia do lambda, w którym przechowujemy
fun
, zamiast przekazywaćfun
bezpośrednio zestaw przeciążenia (którego nie może rozwiązać).Istnieje co najmniej jedna propozycja zdefiniowania operacji w języku C ++, która konwertuje nazwę funkcji na obiekt zestawu przeciążenia. Dopóki taka standardowa propozycja nie będzie w standardzie,
OVERLOADS_OF
makro jest przydatne.Możesz pójść o krok dalej i wesprzeć wskaźnik cast-to-zgodny-funkcyjny.
ale to zaczyna być tępe.
Przykład na żywo .
źródło
Problem polega na tym, że nic nie mówi kompilatorowi, jak wykonać funkcję rozpadu wskaźnika. Jeśli masz
Wtedy kod działałby, ponieważ teraz kompilator wie, jakiej funkcji chcesz, ponieważ istnieje konkretny typ, który przypisujesz.
Podczas używania
std::function
wywołujesz konstruktor funkcji obiektu, który ma postaća ponieważ jest to szablon, musi wydedukować typ przekazywanego obiektu. ponieważ
baz
jest to funkcja przeciążona, nie ma jednego typu, który można by wywnioskować, więc dedukcja szablonu nie powiedzie się i pojawi się błąd. Musisz użyćaby uzyskać siłę jednego typu i pozwolić na odliczenie.
źródło
W momencie, gdy kompilator decyduje, które przeciążenie przejdzie do
std::function
konstruktora, wie tylko, żestd::function
konstruktor ma szablony, aby przyjąć dowolny typ. Nie ma możliwości wypróbowania obu przeciążeń i stwierdzenia, że pierwszy nie kompiluje się, a drugi tak.Sposobem na rozwiązanie tego jest wyraźne poinformowanie kompilatora, które przeciążenie chcesz
static_cast
:źródło