Jeśli mam dwie różne stałe zmienne składowe, które należy zainicjować na podstawie tego samego wywołania funkcji, czy istnieje sposób, aby to zrobić bez dwukrotnego wywołania funkcji?
Na przykład klasa ułamkowa, w której licznik i mianownik są stałe.
int gcd(int a, int b); // Greatest Common Divisor
class Fraction {
public:
// Lets say we want to initialize to a reduced fraction
Fraction(int a, int b) : numerator(a/gcd(a,b)), denominator(b/gcd(a,b))
{
}
private:
const int numerator, denominator;
};
Powoduje to stratę czasu, ponieważ funkcja GCD jest wywoływana dwukrotnie. Możesz także zdefiniować nowego członka klasy gcd_a_b
i najpierw przypisać wyjście gcd do tego na liście inicjalizatora, ale wtedy doprowadziłoby to do marnowania pamięci.
Czy istnieje sposób, aby to zrobić bez marnowania wywołań funkcji lub pamięci? Czy możesz stworzyć tymczasowe zmienne na liście inicjalizatora? Dziękuję Ci.
c++
const
initializer-list
Pytanie 0
źródło
źródło
-O3
. Ale prawdopodobnie dla każdej prostej implementacji testowej w rzeczywistości wstawiłoby wywołanie funkcji. Jeśli użyjesz__attribute__((const))
lub użyjesz prototypu bez podania widocznej definicji, powinien on pozwolić GCC lub clang na eliminację typowej podwyrażenia (CSE) między dwoma wywołaniami z tym samym argumentem. Zauważ, że odpowiedź Drew działa nawet w przypadku nieoczyszczonych funkcji, więc jest o wiele lepsza i powinieneś jej używać, gdy func może nie być wbudowany.Odpowiedzi:
Tak. Można to zrobić za pomocą konstruktora delegującego , wprowadzonego w C ++ 11.
Konstruktor delegujący to bardzo skuteczny sposób na uzyskanie wartości tymczasowych potrzebnych do konstrukcji przed zainicjowaniem jakichkolwiek zmiennych składowych.
źródło
.h
), nawet jeśli rzeczywista definicja konstruktora nie jest widoczna dla wstawiania. tzn.gcd()
wywołanie będzie wstawiane do każdego miejsca wywoławczego konstruktora i pozostawi tylkocall
prywatny konstruktor z 3 operandami.Zmienne składowe są inicjowane według kolejności, w której zostały zadeklarowane podczas deklowania klas, dlatego można wykonać następujące czynności (matematyczne)
Nie ma potrzeby wywoływania innych konstruktorów ani nawet ich tworzenia.
źródło
Fraction(a,b,gcd(a,b))
delegację do osoby dzwoniącej, prowadząc do niższych całkowitych kosztów. To wbudowanie jest łatwiejsze dla kompilatora niż cofnięcie dodatkowego podziału w tym. Nie próbowałem tego na godbolt.org, ale możesz, jeśli jesteś ciekawy. Użyj gcc lub clang,-O3
jak zwykła kompilacja. (C ++ został zaprojektowany w oparciu o założenie nowoczesnego kompilatora optymalizującego, stąd funkcje takie jakconstexpr
)@Drew Dormann podał rozwiązanie podobne do tego, co miałem na myśli. Ponieważ OP nigdy nie wspomina o niemożności modyfikacji ctor, można to wywołać za pomocą
Fraction f {a, b, gcd(a, b)}
:Tylko w ten sposób nie ma drugiego wywołania funkcji, konstruktora lub w inny sposób, więc nie jest to stracony czas. I nie jest to zmarnowana pamięć, ponieważ tymczasowe i tak musiałoby zostać utworzone, więc równie dobrze możesz z niej skorzystać. Pozwala to również uniknąć dodatkowego podziału.
źródło
const
, ale działa przynajmniej dla innych typów. A jakiego dodatkowego podziału „unikasz”? Masz na myśli odpowiedź kontra asmmo?,gcd(foo, bar)
kod jest dodatkowym kodem, który mógłby i dlatego powinien zostać uwzględniony w każdej witrynie wywoławczej w źródle . Jest to problem dotyczący konserwacji / czytelności, a nie wydajności. Kompilator najprawdopodobniej wstawi go w czasie kompilacji, który chcesz zwiększyć wydajność.Fraction f( x+y, a+b );
Aby napisać to po swojemu, będziesz musiał napisaćBadFraction f( x+y, a+b, gcd(x+y, a+b) );
lub użyć tmp vars. Albo jeszcze gorzej, co jeśli chcesz napisaćFraction f( foo(x), bar(y) );
- wtedy potrzebujesz strony wywołującej, aby zadeklarować niektóre zmienne tmp do przechowywania wartości zwrotnych lub wywołać te funkcje ponownie i mieć nadzieję, że kompilator CSE je odejdzie, czego unikamy. Czy chcesz debugować przypadek mieszania argumentów przez jednego z rozmówców,gcd
aby nie był to tak naprawdę GCD pierwszych 2 argumentów przekazanych do konstruktora? Nie? Więc nie pozwól, aby ten błąd był możliwy.