Czy dni przekazywania const std:: string & jako parametr są skończone?
Słyszałem niedawną rozmowę Herba Suttera, który zasugerował, że powody do odejścia std::vector
i std::string
przez const &
w dużej mierze zniknęły. Zasugerował, że pisanie funkcji takiej jak następująca jest teraz lepsze:
std::string do_something ( std::string inval )
{
std::string return_val;
// ... do stuff ...
return return_val;
}
Rozumiem, że return_val
będzie wartością R w punkcie, w którym funkcja zwraca i dlatego może być zwrócona za pomocą semantyki move, która jest bardzo tania. Jednak inval
jest nadal znacznie większy niż rozmiar odniesienia (który jest zwykle zaimplementowany jako wskaźnik). To jest ponieważ std::string
ma różne komponenty, w tym wskaźnik do sterty i element {[7] } do optymalizacji krótkich łańcuchów. Wydaje mi się więc, że przechodzenie przez odniesienie to nadal dobry pomysł.
13 answers
Powodem, dla którego Herb powiedział to, co powiedział, są takie przypadki.
Załóżmy, że mam funkcję A
która wywołuje funkcję B
, która wywołuje funkcję C
. I A
przechodzi przez B
i do C
. A
nie wie ani nie dba o C
; Wszystko A
wie o B
. Oznacza to, że {[5] } jest szczegółem implementacji B
.
Powiedzmy, że A jest zdefiniowane następująco:
void A()
{
B("value");
}
Jeśli B I C biorą łańcuch przez const&
, to wygląda to tak to:
void B(const std::string &str)
{
C(str);
}
void C(const std::string &str)
{
//Do something with `str`. Does not store it.
}
Wszystko dobrze i dobrze. Po prostu przekazujesz wskazówki, żadnego kopiowania, żadnego poruszania się, wszyscy są szczęśliwi. C
bierze const&
ponieważ nie przechowuje ciągu. Po prostu go używa.
Teraz chcę dokonać jednej prostej zmiany: C
musi gdzieś przechowywać ciąg znaków.
void C(const std::string &str)
{
//Do something with `str`.
m_str = str;
}
Witam, Konstruktor kopiujący i potencjalna alokacja pamięci (ignoruj Short String Optimization (SSO) ). Semantyka ruchu C++11 ma umożliwić usunięcie niepotrzebne kopiowanie, prawda? I A
przekazuje tymczasowe; nie ma powodu, dla którego C
powinien kopiować dane. Powinien po prostu uciec z tym, co zostało mu dane.
Tylko, że nie może, ponieważ wymaga const&
.
Jeśli zmienię C
, aby wziąć jego parametr po wartości, to po prostu spowoduje, że B
zrobi kopię do tego parametru; nic nie zyskam.
Więc gdybym tylko przeszedł str
przez wartość przez wszystkie funkcje, opierając się na std::move
aby przetasować danych wokół, nie mielibyśmy tego problemu. Jeśli ktoś chce to zatrzymać, może. Jeśli nie, to dobrze.
To zależy od Twojego przypadku użycia. Jak bardzo nienawidzisz alokacji pamięci?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:24
Czy dni przekazywania const std:: string & jako parametr są skończone?
Nie . Wiele osób korzysta z tej porady (w tym Dave Abrahams) poza domeną, której dotyczy, i upraszcza ją, aby zastosować się do {12]}wszystkich std::string
parametry -- zawsze przekazywanie std::string
przez wartość nie jest "najlepszą praktyką" dla dowolnych dowolnych parametrów i aplikacji, ponieważ optymalizacje, na których te rozmowy/artykuły koncentrują się, dotyczą tylko ograniczonego zbioru przypadki .
Jeśli zwracasz wartość, mutujesz parametr lub przyjmujesz wartość, przekazywanie przez wartość może zaoszczędzić kosztowne kopiowanie i zapewnić wygodę składniową.
Jak zwykle, przejście przez const reference oszczędza wiele kopii , gdy nie potrzebujesz kopii.
Teraz do konkretnego przykładu:
Jednak inval jest nadal dużo większy niż rozmiar odniesienia (który jest zwykle zaimplementowany jako wskaźnik). Dzieje się tak dlatego, że std:: string ma różne komponenty, w tym wskaźnik do sterty i znak pręta [] do optymalizacji krótkich łańcuchów. Wydaje mi się więc, że przechodzenie przez odniesienie to nadal dobry pomysł. Czy ktoś może wyjaśnić, dlaczego Herb mógł to powiedzieć?
Jeśli rozmiar stosu jest problemem (i zakładając, że nie jest to inlined / optimized), return_val
+ inval
> return_val
-- IOW, użycie stosu szczytowego może być zmniejszone przez podanie tutaj wartości (Uwaga: uproszczenie ABIs). Tymczasem przejście przez const reference może wyłączyć optymalizacje. Głównym powodem nie jest unikanie wzrostu stosu, ale zapewnienie optymalizacji może być przeprowadzone , gdzie ma to zastosowanie.
Dni mijania const reference jeszcze się nie skończyły-zasady są po prostu bardziej skomplikowane niż kiedyś. Jeśli wydajność jest ważna, warto zastanowić się, jak przekazać te typy, na podstawie szczegółów używanych w swoich implementacjach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 21:28:12
To w dużej mierze zależy od implementacji kompilatora.
Jednak zależy to również od tego, czego używasz.
Rozważmy następne funkcje:
bool foo1( const std::string v )
{
return v.empty();
}
bool foo2( const std::string & v )
{
return v.empty();
}
Te funkcje są zaimplementowane w oddzielnej jednostce kompilacji w celu uniknięcia inliningu. Następnie:
1. Jeśli przekażesz dosłowność do tych dwóch funkcji, nie zobaczysz dużej różnicy w występach. W obu przypadkach należy utworzyć obiekt string
2. Jeśli przekażesz inny obiekt std:: string, foo2
będzie lepszy foo1
, ponieważ foo1
zrobi głęboką kopię.
Na moim komputerze, używając g++ 4.6.1, mam takie wyniki:
- zmienna według referencji: 1000000000 iteracji - > czas upłynął: 2.25912 sek
- zmienna według wartości: 1000000000 iteracji - > czas upłynął: 27.2259 sek
- {{100000000} - > czas upłynął: 9.10319 sek
- dosłowne według wartości: 100000000 iteracji - > czas upłynął: 8.62659 sek
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-19 16:05:05
Jeśli nie potrzebujesz kopii, nadal rozsądnie jest wziąć const &
. Na przykład:
bool isprint(std::string const &s) {
return all_of(begin(s),end(s),(bool(*)(char))isprint);
}
Jeśli zmienisz to, aby przyjmować ciąg znaków według wartości, w końcu przeniesiesz lub skopiujesz parametr i nie ma takiej potrzeby. Nie tylko kopiowanie / przenoszenie jest prawdopodobnie droższe, ale również wprowadza nową potencjalną awarię; kopiowanie / przenoszenie może rzucić wyjątek (np. alokacja podczas kopiowania może się nie powieść), podczas gdy odwołanie do istniejącej wartości nie może.
If you do potrzebujesz kopii, a następnie przekazywanie i zwracanie przez wartość jest zwykle (zawsze?) najlepszym rozwiązaniem. W rzeczywistości generalnie nie martwiłbym się o to w C++03, chyba że okaże się, że dodatkowe kopie faktycznie powodują problem z wydajnością. Copy elision wydaje się dość wiarygodne na współczesnych kompilatorach. Myślę, że sceptycyzm ludzi i upór, że trzeba sprawdzić tabelę wsparcia kompilatora dla RVO jest w większości przestarzały w dzisiejszych czasach.
W skrócie, C++11 tak naprawdę nic nie zmienia w tym zakresie z wyjątkiem ludzie, którzy nie ufali copy elision.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-19 15:50:31
Krótka odpowiedź: nie! Długa odpowiedź:
-
Jeśli nie chcesz modyfikować ciągu znaków (treat jest tylko do odczytu), przekaż go jako
const ref&
.
(funkcja ta musi oczywiście pozostawać w zasięgu, podczas gdy funkcja, która go używa, wykonuje) - jeśli planujesz ją zmodyfikować lub wiesz, że wyjdzie poza zakres (wątki) , przekaż ją jako
value
, nie kopiujconst ref&
wewnątrz twojego ciała funkcji.
Był post na cpp-next.com called "chcesz prędkości, podaj wartość!". TL; DR:
Guideline: nie kopiuj argumentów funkcji. Zamiast tego przekaż je według wartości i pozwól kompilatorowi wykonać kopiowanie.
Tłumaczenie ^
Nie kopiuj argumentów funkcji - - - oznacza: jeśli planujesz zmodyfikować wartość argumentu kopiując ją do zmiennej wewnętrznej, po prostu użyj argumentu wartości.
Więc, nie rób to:
std::string function(const std::string& aString){
auto vString(aString);
vString.clear();
return vString;
}
Do this :
std::string function(std::string aString){
aString.clear();
return aString;
}
Kiedy trzeba zmodyfikować wartość argumentu w ciele funkcji.
musisz tylko być świadomy, jak planujesz użyć argumentu w ciele funkcji. Tylko do odczytu czy nie... i jeśli będzie w zasięgu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-27 13:59:05
Prawie.
W C++17 mamy basic_string_view<?>
, co sprowadza nas do jednego wąskiego przypadku użycia dla std::string const&
parametrów.
Istnienie semantyki move wyeliminowało jeden przypadek użycia std::string const&
-- jeśli planujesz zapisać parametr, pobranie std::string
według wartości jest bardziej optymalne, jak możesz move
z parametru.
Jeśli ktoś wywołał twoją funkcję z surowym C "string"
oznacza to, że tylko jeden bufor std::string
jest kiedykolwiek alokowany, w przeciwieństwie do dwóch w std::string const&
case.
Jeśli jednak nie zamierzasz wykonać kopii, wykonywanie przez {[1] } jest nadal przydatne w C++14.
Z std::string_view
, tak długo, jak nie przekazujesz tego ciągu do API, które oczekuje zakończonych buforami znaków w stylu C, możesz efektywniej uzyskać std::string
podobną funkcjonalność bez ryzyka alokacji. Surowy ciąg C może być nawet zamieniony w std::string_view
bez alokacji lub kopiowania znaków.
W tym momencie, użycie dla std::string const&
jest wtedy, gdy nie kopiujesz hurtownia danych i zamierzają przekazać je do API w stylu C, które oczekuje zakończonego buforem null, i potrzebujesz funkcji łańcuchowych wyższego poziomu, które std::string
zapewnia. W praktyce jest to rzadki zestaw wymagań.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-11 09:30:03
std::string
nie jest zwykłymi starymi danymi (POD) , a jego surowy rozmiar nie jest najistotniejszą rzeczą w historii. Na przykład, jeśli przekazujesz ciąg znaków, który jest powyżej długości SSO i przydzielony na stercie, oczekuję, że Konstruktor kopiujący nie skopiuje magazynu SSO.
Jest to zalecane, ponieważ inval
jest skonstruowany z wyrażenia argumentu, a więc jest zawsze przenoszony lub kopiowany odpowiednio - nie ma utraty wydajności, zakładając, że potrzebujesz własności kłótnia. Jeśli tego nie zrobisz, odniesienie const
nadal może być lepszym rozwiązaniem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:03
Skopiowałem / wkleiłem odpowiedź z tego pytania tutaj i zmieniłem nazwy i pisownię, aby pasowały do tego pytania.
Oto kod do pomiaru tego, co jest pytane:
#include <iostream>
struct string
{
string() {}
string(const string&) {std::cout << "string(const string&)\n";}
string& operator=(const string&) {std::cout << "string& operator=(const string&)\n";return *this;}
#if (__has_feature(cxx_rvalue_references))
string(string&&) {std::cout << "string(string&&)\n";}
string& operator=(string&&) {std::cout << "string& operator=(string&&)\n";return *this;}
#endif
};
#if PROCESS == 1
string
do_something(string inval)
{
// do stuff
return inval;
}
#elif PROCESS == 2
string
do_something(const string& inval)
{
string return_val = inval;
// do stuff
return return_val;
}
#if (__has_feature(cxx_rvalue_references))
string
do_something(string&& inval)
{
// do stuff
return std::move(inval);
}
#endif
#endif
string source() {return string();}
int main()
{
std::cout << "do_something with lvalue:\n\n";
string x;
string t = do_something(x);
#if (__has_feature(cxx_rvalue_references))
std::cout << "\ndo_something with xvalue:\n\n";
string u = do_something(std::move(x));
#endif
std::cout << "\ndo_something with prvalue:\n\n";
string v = do_something(source());
}
Dla mnie to:
$ clang++ -std=c++11 -stdlib=libc++ -DPROCESS=1 test.cpp
$ a.out
do_something with lvalue:
string(const string&)
string(string&&)
do_something with xvalue:
string(string&&)
string(string&&)
do_something with prvalue:
string(string&&)
$ clang++ -std=c++11 -stdlib=libc++ -DPROCESS=2 test.cpp
$ a.out
do_something with lvalue:
string(const string&)
do_something with xvalue:
string(string&&)
do_something with prvalue:
string(string&&)
Poniższa tabela podsumowuje moje wyniki (używając clang-std=c++11). Pierwsza liczba to liczba konstrukcji kopiujących, a druga liczba to liczba konstrukcji ruchomych:
+----+--------+--------+---------+
| | lvalue | xvalue | prvalue |
+----+--------+--------+---------+
| p1 | 1/1 | 0/2 | 0/1 |
+----+--------+--------+---------+
| p2 | 1/0 | 0/1 | 0/1 |
+----+--------+--------+---------+
Rozwiązanie pass-by-value wymaga tylko jednego przeciążenia, ale kosztuje dodatkowa konstrukcja ruchu podczas przechodzenia wartości LV i xvalue. Może to być, ale nie może być dopuszczalne w danej sytuacji. Oba rozwiązania mają zalety i wady.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:48
Herb Sutter jest nadal w rejestrze, wraz z Bjarne Stroustroup, zalecając const std::string&
jako typ parametru; zobacz https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rf-in .
Istnieje pułapka, o której nie wspomniano w żadnej z innych odpowiedzi tutaj: jeśli przekażesz literalny łańcuch znaków do parametru const std::string&
, przekaże on odniesienie do tymczasowego łańcucha, utworzonego w locie, aby trzymać znaki literała. Jeśli następnie zapisać to odniesienie, będzie to nieprawidłowy po dealokacji tymczasowego łańcucha. Aby być bezpiecznym, musisz zapisać kopię , a nie odniesienie. Problem wynika z faktu, że literały ciągów są typami const char[N]
, wymagającymi promocji do std::string
.
Poniższy kod ilustruje pułapkę i obejście, wraz z niewielką opcją wydajności -- przeciążenie metodą const char*
, jak opisano w czy istnieje sposób, aby przekazać ciąg znaków jako odniesienie w C++.
(Uwaga: Sutter & Stroustroup radzą że jeśli zachowasz kopię ciągu znaków, podaj również przeciążoną funkcję z parametrem & & i STD:: move ().)
#include <string>
#include <iostream>
class WidgetBadRef {
public:
WidgetBadRef(const std::string& s) : myStrRef(s) // copy the reference...
{}
const std::string& myStrRef; // might be a reference to a temporary (oops!)
};
class WidgetSafeCopy {
public:
WidgetSafeCopy(const std::string& s) : myStrCopy(s)
// constructor for string references; copy the string
{std::cout << "const std::string& constructor\n";}
WidgetSafeCopy(const char* cs) : myStrCopy(cs)
// constructor for string literals (and char arrays);
// for minor efficiency only;
// create the std::string directly from the chars
{std::cout << "const char * constructor\n";}
const std::string myStrCopy; // save a copy, not a reference!
};
int main() {
WidgetBadRef w1("First string");
WidgetSafeCopy w2("Second string"); // uses the const char* constructor, no temp string
WidgetSafeCopy w3(w2.myStrCopy); // uses the String reference constructor
std::cout << w1.myStrRef << "\n"; // garbage out
std::cout << w2.myStrCopy << "\n"; // OK
std::cout << w3.myStrCopy << "\n"; // OK
}
Wyjście:
const char * constructor const std::string& constructor Second string Second string
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:32
IMO używanie referencji C++ dla std::string
jest szybką i krótką optymalizacją lokalną, podczas gdy użycie wartości passing by Może być (lub nie) lepszą optymalizacją globalną.
Więc odpowiedź brzmi: to zależy od okoliczności:
- Jeśli piszesz cały kod z zewnątrz do wewnątrz funkcji, wiesz, co robi Kod, możesz użyć referencji
const std::string &
. - Jeśli piszesz kod biblioteki lub używasz mocno kodu biblioteki, w której przekazywane są ciągi znaków, prawdopodobnie zyskasz więcej w globalnym wyczuwaj, ufając
std::string
zachowaniu konstruktora kopiującego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-25 12:03:06
Zobacz "Herb Sutter" powrót do podstaw! Podstawy nowoczesnego stylu C++ " . Wśród innych tematów, przegląda parametr przekazując porady, które zostały podane w przeszłości, i nowe pomysły, które przychodzą w C++11 i konkretnie patrzy na pomysł przekazywania ciągów przez wartość.
Benchmarki pokazują, że przekazywanie std::string
s przez wartość, w przypadkach, gdy funkcja i tak ją skopiuje, może być znacznie wolniejsze!
To dlatego, że zmuszasz go do zawsze wykonaj pełną kopię (a następnie przenieś na swoje miejsce), podczas gdy wersja const&
zaktualizuje Stary ciąg znaków, który może ponownie użyć już przydzielonego bufora.
Zobacz jego slajd 27: dla funkcji" set " opcja 1 jest taka sama jak zawsze. Opcja 2 dodaje przeciążenie dla odniesienia rvalue, ale daje to wybuch kombinatoryczny, jeśli istnieje wiele parametrów.
To tylko dla parametrów "sink", gdzie musi być utworzony łańcuch znaków (nie zmieniając jego istniejącej wartości), że Trick pass-by-value jest ważne. Oznacza to, że konstruktory, w których parametr bezpośrednio inicjuje element dopasowującego typu.
Jeśli chcesz zobaczyć, jak głęboko możesz się tym martwić, obejrzyj prezentację Nicolai Josuttisa i powodzenia z tym ("Perfect - Done!" n razy po wykryciu błędu w poprzedniej wersji. Byłeś tam kiedyś?)
Jest to również podsumowane jako ⧺F. 15 w standardowych wytycznych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 01:25:53
Jak zaznacza @JDługosz w komentarzach, Herb daje inne rady w innym (później?) talk, see mniej więcej stąd: https://youtu.be/xnqTKD8uD64?t=54m50s .
Jego rada sprowadza się tylko do użycia parametrów wartości dla funkcji f
, która pobiera tzw. argumenty sink, zakładając, że przeniesiesz konstrukcję z tych argumentów sink.
To ogólne podejście dodaje tylko narzut konstruktora ruchu zarówno dla argumentów lvalue, jak i rvalue w porównaniu z optymalnym implementacja f
dostosowana odpowiednio do argumentów lvalue i rvalue. Aby zobaczyć, dlaczego tak jest, Załóżmy, że f
pobiera parametr wartości, gdzie T
jest pewnym typem konstruktywnym typu copy and move:
void f(T x) {
T y{std::move(x)};
}
Wywołanie f
z argumentem lvalue spowoduje, że Konstruktor kopiujący zostanie wywołany do konstruowania x
, a konstruktor move zostanie wywołany do konstruowania y
. Z drugiej strony, wywołanie f
z argumentem rvalue spowoduje wywołanie konstruktora move do konstruowania x
, oraz inny konstruktor ruchu, który ma być wywołany do konstruowania y
.
Ogólnie, optymalna implementacja f
dla argumentów lvalue jest następująca:
void f(const T& x) {
T y{x};
}
W tym przypadku tylko jeden Konstruktor kopiujący jest wywoływany do konstruowania y
. Optymalna implementacja f
dla argumentów rvalue jest, ogólnie rzecz biorąc, następująca:
void f(T&& x) {
T y{std::move(x)};
}
W tym przypadku tylko jeden konstruktor ruchu jest wywoływany do konstruowania y
.
Więc rozsądnym kompromisem jest przyjęcie wartości parametr i mają jeden dodatkowy konstruktor ruchu wywołujący argumenty lvalue lub rvalue w odniesieniu do optymalnej implementacji, co jest również radą udzieloną w rozmowie Herba.
Jak zauważył @JDługosz w komentarzach, przekazywanie przez wartość ma sens tylko dla funkcji, które zbudują jakiś obiekt z argumentu sink. Jeśli masz funkcję f
, która kopiuje jej argument, podejście pass-by-value będzie miało więcej narzutu niż ogólne podejście pass-by-const-reference. Metoda pass-by-value dla funkcji f
, która zachowuje kopię swojego parametru, będzie miała postać:
void f(T x) {
T y{...};
...
y = std::move(x);
}
W tym przypadku istnieje konstrukcja kopiująca i przypisanie move dla argumentu lvalue oraz konstrukcja move i przypisanie move dla argumentu rvalue. Najbardziej optymalnym przypadkiem argumentu lvalue jest:
void f(const T& x) {
T y{...};
...
y = x;
}
Sprowadza się to tylko do przypisania, które jest potencjalnie znacznie tańsze niż Konstruktor kopiujący plus przypisanie ruchu wymagane do podejście pass-by-value. Powodem tego jest to, że przypisanie może ponownie wykorzystać istniejącą przydzieloną pamięć w y
, a tym samym zapobiec (de)alokacji, podczas gdy Konstruktor kopiujący zwykle przydziela pamięć.
Dla argumentu rvalue najbardziej optymalna implementacja dla f
, która zachowuje kopię ma postać:
void f(T&& x) {
T y{...};
...
y = std::move(x);
}
Więc, w tym przypadku tylko przeniesienie. Przekazanie wartości R do wersji f
, która pobiera referencję const, kosztuje tylko przypisanie zamiast przesuń zadanie. Relatywnie rzecz biorąc, wersja f
przyjmująca odniesienie const w tym przypadku jako ogólna implementacja jest preferowana.
Więc ogólnie rzecz biorąc, aby uzyskać najbardziej optymalną implementację, będziesz musiał przeciążyć lub zrobić jakieś idealne przekierowanie, jak pokazano w dyskusji. Wadą jest kombinatoryczna eksplozja liczby wymaganych przeciążeń, w zależności od liczby parametrów dla f
W przypadku, gdy zdecydujesz się na przeciążenie kategorii wartości argumentu. Perfect przekierowanie ma tę wadę, że f
staje się funkcją szablonu, co uniemożliwia zrobienie go wirtualnym, a skutkuje znacznie bardziej złożonym kodem, jeśli chcesz uzyskać go w 100% poprawnie(zobacz rozmowę o krwawych szczegółach).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-27 13:48:56
Problem polega na tym, że" const " jest kwalifikatorem nie-ziarnistym. To, co zwykle oznacza "const string ref", to "nie modyfikuj tego łańcucha", a Nie"Nie modyfikuj liczby referencji". Po prostu nie ma sposobu, aby w C++ powiedzieć , Które członkowie są "const". Albo wszyscy są, albo żaden z nich nie jest.
W celu zhakowania tego problemu językowego, STL może pozwolić "C ()" w twoim przykładzie Na wykonanie move-semantycznej kopii w każdym razie i posłusznie ignorować "const" z regard to the reference count (mutable). Tak długo, jak to było dobrze sprecyzowane, to byłoby w porządku.
Ponieważ STL tego nie robi, mam wersję napisu, który const_casts usuwa licznik referencji (nie ma możliwości retroaktywnego stworzenia czegoś zmiennego w hierarchii klas), I-lo i Beautiful-możesz swobodnie przekazywać cmstringi jako referencje const i robić ich kopie w głębokich funkcjach, przez cały dzień, bez wycieków lub problemów.
Ponieważ C++ nie oferuje " pochodnej klasy const" tutaj spisanie dobrej specyfikacji i zrobienie błyszczącego nowego obiektu" const movable string " (cmstring) jest najlepszym rozwiązaniem, jakie widziałem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-10 20:53:28