Dlaczego program wypisuje tak dużo destruktorów

0

#include <iostream>
#include <string>
#include <vector>


template <typename T>
class IsBigger
{
public:
IsBigger(const T parametr) : skladnik(parametr){std::cout << "Constr";}
~IsBigger(){std::cout << "Destr";}
bool operator()(T liczba){return liczba > skladnik;}
private:
T skladnik;
};

template <typename T, typename P>
int count(const T & v, P predicate)
{
int total{0};
for (const auto & x : v) if (predicate(x)) total++;
return total;	
};


int main()

{
	std::vector<int> wek;
	for (int i=0;i<20;i++) wek.push_back(i);
	std::cout << count(wek, IsBigger<int>(7)) << std::endl;
	std::string tablica[8]{"abba", "babba", "cabba", "dabba", "dbba", "eghs"};
	std::cout << count(tablica, IsBigger<std::string>("cabb"));
	
	
}

Program wypisuje:

ConstrDestr12
DestrConstrDestr4

Dlaczego w drugiej linii wypisuje drugi destruktor? Tyko, proszę, łopatologicznie, to może coś zrozumiem.

2

Powodem jest ta linijka

int count(const T & v, P predicate)

Jeśli zmienisz ją w ten sposób

int count(const T & v, P &&predicate)

dostaniesz wyniki jak w innych kompilatorach.

Faktycznie MSVC robi coś dziwnego z wartościami tymczasowymi, tak na szybko to nie umiem Ci odpowiedzieć dlaczego i chyba bez zagłębienia się w wygenerowany kod jak i standard się nie obejdzie.

1

Wypisz też autogenerowane konstruktory:

template <typename T>
class IsBigger
{
public:
IsBigger(const T parametr) : skladnik(parametr){std::cout << "Constr";}
//........
IsBigger(const IsBigger &other) : skladnik(other.skladnik){std::cout << "CopyConstr";}
IsBigger(IsBigger &&other) : skladnik(std::move(other.skladnik)){std::cout << "MoveConstr";}
//........

1
mwl4 napisał(a):

Wypisz też autogenerowane konstruktory:

Powinienem o tym wspomnieć. Wtedy też się robi ciekawie, dodatkowe zawołanie destruktora znika pomimo, że żaden z nowo logowanych kontruktorów się nie zalogował. Trochę cyrk. Bez spojrzenia co tam cl wygenerował chyba się jednak nie obejdzie.

1

@several: Możliwe, że dodanie noexcept do move constructora zmieni zachowanie znowu.

template <typename T>
class IsBigger
{
public:
IsBigger(const T parametr) : skladnik(parametr){std::cout << "Constr";}
//........
IsBigger(const IsBigger &other) : skladnik(other.skladnik){std::cout << "CopyConstr";}
IsBigger(IsBigger &&other) noexcept : skladnik(std::move(other.skladnik)){std::cout << "MoveConstr";}
//........
0

OMG problemem jest zrozumienie wyjścia.
Obie linijki robią dokładnie to samo, tylko zostały sklejone na wyjściu nie tak jak oczekujesz. Na dodatek opisane wyjście jest inne niż produkuje kod: https://godbolt.org/z/a8zrKKqvb

Po nieistotnej poprawce, która przenosi end do osobnej instrukcji https://godbolt.org/z/MPxfhn6Wv wychodzi:

Constr12Destr
Constr4Destr

Po jednym konstruktorze i destruktorze zgodnie z oczekiwaniem.

Po prostu jak w originale masz:

std::cout << count(wek, IsBigger<int>(7)) << std::endl;

To obiekt IsBigger<int>(7) jest kasowany po wykonaniu << std::endl, więc log z destruktora ląduje w następnej linijce.

Wszystkie obiekty tymczasowe, są usuwane w odwrotnej kolejności po wykonaniu wszystkich operacji aż do średnika (zapomniałem jaka jest profesjonalna terminologia w tym miejscu).

0

@MarekR22

Na dodatek opisane wyjście jest inne niż produkuje kod

Opisane wyjście pojawia się tylko na MSVC co zostało wyjaśnione w komentarzach oraz pokrętnie zasugerowane w mojej pierwszej odpowiedzi. Na mojej maszynie dostaję taki sam log jak OP.

1

Moze to roznica w wersji standardu? Zmienila sie chyba obsluga copy elision?

1 użytkowników online, w tym zalogowanych: 0, gości: 1