Megvalósítottam egy egyéni tárolót (ugyanúgy, mint az std::vector), és megpróbálom úgy elkészíteni, hogy a 'push_back' függvénye kihasználja a mozgatási szemantikát, nehogy másolatot készítsen arról, ami vissza van tolva – különösen akkor, ha a a konténerbe tolandó tárgyat egy külső funkció visszaadja.
Miután sokat olvastam az áthelyezési szemantikáról és az egyéni tárolókról, még mindig nem találom, hogy a megközelítésem miért generál még mindig másolatot ahelyett, hogy az átadott objektumot a tároló belső dinamikus tömbjébe helyezné át.
Íme a konténerem egyszerűsített változata, így néz ki:
template<class T>
class Constructor
{
private:
size_t size = 0;
size_t cap = 0;
T *data = nullptr;
public:
Constructor()
{
cap = 1;
size = 0;
data = static_cast<T*>(malloc(cap * sizeof(T)));
}
~Constructor()
{ delete[] data; }
template<typename U>
void push_back(U &&value)
{
if (size + 1 >= cap)
{
size_t new_cap = (cap * 2);
T* new_data = static_cast<T*>(malloc(new_cap * sizeof(T)));
memmove(new_data, data, (size) * sizeof(T));
for (size_t i = 0; i<cap; i++)
{
data[i].~T();
}
delete[] data;
cap = new_cap;
data = new_data;
new(data + size) T(std::forward<U>(value));
}
else
{
new(data + size) T(std::forward<U>(value));
}
++size;
}
const T& operator[](const size_t index) const //access [] overloading
{
return data[index];
}
};
Itt van egy egyéni osztály, amely üzeneteket nyomtat a példányai létrehozásakor, másolásakor vagy áthelyezésekor, hogy segítse a hibakeresést:
class MyClass
{
size_t id;
public:
MyClass(const size_t new_id)
{
id = new_id;
std::cout << "new instance with id " << id << std::endl;
}
MyClass(const MyClass &passedEntity)
{
id = passedEntity.id;
std::cout << "copied instance" << std::endl;
}
MyClass(MyClass &&passedEntity)
{
id = passedEntity.id;
std::cout << "moved instance" << std::endl;
}
void printID() const
{
std::cout << "this instance's id is " << id << std::endl;
}
};
És itt van a külső funkció:
MyClass &foo(MyClass &passed)
{
return passed;
}
Végül itt van a main
függvény, amely egy tesztesetet futtat a fenti függvény és osztályok használatával a probléma megjelenítéséhez:
int main()
{
MyClass a(33);
std::cout << std::endl;
std::cout << "Using my custom container: " << std::endl;
Constructor<MyClass> myContainer;
myContainer.push_back(foo(a));
myContainer[0].printID();
std::cout << std::endl;
std::cout << "Using dinamic array: " << std::endl;
MyClass *dinArray = static_cast<MyClass*>(malloc(1 * sizeof(MyClass)));
dinArray = new(dinArray + 1) MyClass(std::forward<MyClass>(foo(a)));
dinArray[0].printID();
std::cout << std::endl;
system("Pause");
return 0;
}
A kimenet a következő:
new instance with id 33
Using my custom container:
copied instance
this instance's id is 33
Using dinamic array:
moved instance
this instance's id is 33
Amint látható, ha a MyClass
példányát közvetlenül egy dinamikus tömbbe helyezzük, akkor csak a mozgatás konstruktor kerül meghívásra, a másolás konstruktor nem. Ha azonban visszatolom_a yClass
példányt a Container
példányába, a másoláskonstruktor továbbra is meghívásra kerül.
Valaki segítene megérteni, hogy pontosan mit csinálok rosszul? Hogyan tudnám elérni, hogy az elemek másolat létrehozása nélkül kerüljenek a tárolóba?
push_back
függvényemen belül miért nem tudtam egyszerűen megtenni anew(data + size) T(std::move(value));
-t ahelyett, hogy astd::move
-t csak apush_back
hívásakor használnám? 05.12.2017move
nem használható a push_back metóduson belül, mert amove
mindig R-referenciát ad vissza. Aforward
visszatérési típusa avalue
típusától függ. Ha a push_back-t L-értékkel hívod (aa
objektumot.push_back(a)
használatával adod át), akkor az L-referenciátvalue
típusként kapod, és ez rendben van, mert nem akarod áthelyezni avalue
objektumot. a push_back módszert. Ha apush_back
-t R-értékkel hívja (az 'a'-t a.push_back(move(a))
használatával adja meg), avalue
R-referenciatípussal rendelkezik, és a mozgatható konstruktor meghívható. 05.12.2017std::move()
ahol korábban voltstd::forward<U>()
, és most tökéletesen működik. Egész nap használtam minden észrevehető probléma nélkül. 06.12.2017