Szukam reguł dotyczących przekazywania funkcji szablonów C ++ jako argumentów.
Jest to obsługiwane przez C ++, jak pokazano na przykładzie tutaj:
#include <iostream>
void add1(int &v)
{
v+=1;
}
void add2(int &v)
{
v+=2;
}
template <void (*T)(int &)>
void doOperation()
{
int temp=0;
T(temp);
std::cout << "Result is " << temp << std::endl;
}
int main()
{
doOperation<add1>();
doOperation<add2>();
}
Jednak nauka tej techniki jest trudna. Googling dla „funkcja jako argument szablonu” nie prowadzi do wielu. I klasyczne szablony C ++ Kompletny przewodnik zaskakująco również go nie omawia (przynajmniej nie z mojego wyszukiwania).
Mam pytania, czy to jest poprawne C ++ (czy tylko jakieś szeroko obsługiwane rozszerzenie).
Czy jest też sposób, aby zezwolić na stosowanie funktora o tej samej sygnaturze zamiennie z funkcjami jawnymi podczas tego rodzaju wywoływania szablonu?
Poniższe nie działa w powyższym programie, przynajmniej w Visual C ++ , ponieważ składnia jest oczywiście nieprawidłowa. Byłoby miło móc wyłączyć funkcję funktora i vice versa, podobnie jak można przekazać wskaźnik funkcji lub funktor do algorytmu std :: sort, jeśli chcesz zdefiniować niestandardową operację porównania.
struct add3 {
void operator() (int &v) {v+=3;}
};
...
doOperation<add3>();
Będziemy wdzięczni za wskazania na link lub dwa lub stronę w książce szablonów C ++!
źródło
-std=gnu++17
. Czy mogę użyć wyniku bez przechwyconego operatora konwersji C ++ 17 lambda constexpr jako argumentu innego niż typ szablonu wskaźnika? .Odpowiedzi:
Tak, to jest poprawne.
Jeśli chodzi o to, aby działało to również z funktorami, zwykłym rozwiązaniem jest coś takiego:
które można teraz wywołać jako:
Zobacz na żywo
Problem polega na tym, że jeśli kompilator utrudnia inline wywołanie
add2
, ponieważ kompilator wie, żevoid (*)(int &)
przekazywany jest typ wskaźnika funkcjidoOperation
. (Aleadd3
będąc funktorem, można go łatwo wprowadzić. Tutaj kompilator wie, że obiekt typuadd3
jest przekazywany do funkcji, co oznacza, że wywoływaną funkcją jestadd3::operator()
, a nie tylko nieznany wskaźnik funkcji).źródło
template <typename F> void doOperation(F&& f) {/**/}
), więc na przykład bind może przekazać wyrażenie wiązania zamiast go powiązać?Parametry szablonu można sparametryzować według typu (nazwa typu T) lub wartości (int X).
„Tradycyjnym” sposobem szablonów fragmentu kodu w C ++ jest użycie funktora - tzn. Kod znajduje się w obiekcie, a zatem obiekt nadaje unikalny typ kodowi.
Podczas pracy z tradycyjnymi funkcjami ta technika nie działa dobrze, ponieważ zmiana typu nie wskazuje konkretnej funkcji - raczej określa jedynie sygnaturę wielu możliwych funkcji. Więc:
Nie jest odpowiednikiem przypadku funktora. W tym przykładzie instancja do_op jest tworzona dla wszystkich wskaźników funkcji, których sygnaturą jest int X (int, int). Kompilator musiałby być dość agresywny, aby w pełni uwzględnić tę sprawę. (Nie wykluczyłbym tego, ponieważ optymalizacja kompilatora stała się bardzo zaawansowana.)
Jednym ze sposobów stwierdzenia, że ten kod nie robi dokładnie tego, czego chcemy, jest:
jest nadal legalne i wyraźnie nie jest to podkreślane. Aby uzyskać pełne wstawianie, musimy utworzyć szablon według wartości, aby funkcja była w pełni dostępna w szablonie.
W takim przypadku każda utworzona instancja do_op jest tworzona z określoną dostępną funkcją. Dlatego spodziewamy się, że kod do_op będzie przypominał „return a + b”. (Programiści Lisp, przestańcie się śmiać!)
Możemy również potwierdzić, że jest to bliższe temu, czego chcemy, ponieważ:
nie uda się skompilować. GCC mówi: „błąd: 'func_ptr' nie może pojawić się w wyrażeniu stałym. Innymi słowy, nie mogę w pełni rozwinąć do_op, ponieważ nie dostarczyłeś mi wystarczająco dużo informacji w czasie kompilatora, aby wiedzieć, co to jest nasza operacja.
Więc jeśli drugi przykład naprawdę w pełni uwzględnia naszą operację, a pierwszy nie, to po co szablon? Co to robi? Odpowiedź brzmi: wpisz przymus. Ten riff na pierwszym przykładzie będzie działał:
Ten przykład zadziała! (Nie sugeruję, że jest dobry C ++, ale ...) Co się stało jest do_op został matrycy wokół podpisy różnych funkcji, a każdy oddzielny instancji napisze inny kod rodzaj przymusu. Tak więc instancyjny kod do_op z fadd wygląda mniej więcej tak:
Dla porównania, nasz przypadek wartości wymaga dokładnego dopasowania argumentów funkcji.
źródło
int c = do_op(4,5,func_ptr);
„wyraźnie nie jest wprowadzana”.Wskaźniki funkcji mogą być przekazywane jako parametry szablonu i jest to część standardowego C ++ . Jednak w szablonie są one zadeklarowane i używane jako funkcje zamiast wskaźnika do funkcji. Podczas tworzenia szablonu podaje się adres funkcji, a nie tylko nazwę.
Na przykład:
Jeśli chcesz przekazać typ funktora jako argument szablonu:
Kilka odpowiedzi przekazuje instancję funktora jako argument:
Najbliżej tego jednolitego wyglądu za pomocą argumentu szablonu można zdefiniować
do_op
dwa razy - raz parametrem innym niż typ i raz parametrem typu.Szczerze mówiąc, naprawdę nie spodziewałem się, że to się nie skompiluje, ale zadziałało to dla mnie z gcc-4.8 i Visual Studio 2013.
źródło
W twoim szablonie
Ten parametr
T
jest nietypowym parametrem szablonu. Oznacza to, że zachowanie funkcji szablonu zmienia się wraz z wartością parametru (który musi zostać ustalony w czasie kompilacji, jakie są stałe wskaźnika funkcji).Jeśli chcesz czegoś, co działa zarówno z obiektami funkcji, jak i parametrami funkcji, potrzebujesz szablonu maszynowego. Jednak po wykonaniu tej czynności należy również dostarczyć instancję obiektu (instancję obiektu funkcji lub wskaźnik funkcji) do funkcji w czasie wykonywania.
Istnieje kilka drobnych uwag dotyczących wydajności. Ta nowa wersja może być mniej wydajna z argumentami wskaźnika funkcji, ponieważ konkretny wskaźnik funkcji jest tylko wyłączony i wywoływany w czasie wykonywania, podczas gdy szablon wskaźnika funkcji można zoptymalizować (ewentualnie wbudowane wywołanie funkcji) w oparciu o konkretny zastosowany wskaźnik funkcji. Obiekty funkcyjne mogą być często bardzo skutecznie rozszerzane za pomocą szablonu maszynowego, jednak o szczegółach
operator()
decyduje typ obiektu funkcyjnego.źródło
Twój przykład funktora nie działa dlatego, że potrzebujesz instancji, aby wywołać funkcję
operator()
.źródło
Edycja: Przekazywanie operatora jako odwołania nie działa. Dla uproszczenia zrozum go jako wskaźnik funkcji. Po prostu wysyłasz wskaźnik, a nie referencję. Myślę, że próbujesz napisać coś takiego.
. .
źródło