• Advertisement
Sign in to follow this  

C++ Scope of local variables and their destructors

Recommended Posts

Its been a while since I have done C++ and I was wondering if someone could help me understand local variables and what happens they go out of scope

So I have this method for creating a vertex shader and it returns a custom class called VertexShader. But before it returns the VertexShader it places it into a std::unsorted_map called vertexShaders

//Declared at the ShaderModule class level. Map to store VertexShaders in
std::unordered_map<LPCSTR, VertexShader> vertexShaders;

//Method to create a vertex shader. Returns a VertexShader
VertexShader ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile)
{
	ID3DBlob *shaderCode = compileShaderFromFile(fileName, entryPoint, targetProfile);

	D3D11_INPUT_ELEMENT_DESC inputElementDescription[] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};

	VertexShader vertexShader(id); //Create a new VertexShader
	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	vertexShaders[id] = vertexShader; //Place into the VertexShader map. Places copy?
	return vertexShader;
}

Now at the end of the method, right as the return is done, I see that the destructor for my variable vertexShader gets called. And this might sound ridiculous, but I really dont understand why?

Shouldn't it live on in memory since it was placed into the vertexShaders map and only when the vertexShaders map is destroyed then the deconstructor for my VertexShader is called?  By doing vertexShaders[id] = vertexShader am I really just making a copy and then eventually another deconstructor for the VertexShader placed into the map will be called?

 

Also when using the method the destructor is called twice? VertexShader being returned from the method now also getting destroyed?

//Declaration of the vertex shader. Done at the class level
VertexShader vShader;

//Method that uses the createVertexShader
void GraphicsSystem::initialize(HWND windowHandle)
{
	/* Other init code*/

	vShader = shaderModule.createVertexShader(L"default.shader", "VertexShader", "VShader", "vs_4_0");
}

 

Edited by noodleBowl

Share this post


Link to post
Share on other sites
Advertisement

Placing your variable into the std map dose not give it a scope as it does in C# for example. C++ dosent know if an object is in scope or out of it, this would be the task for a memory/garbadge collector system you would have to write by yourself. This line

VertexShader vertexShader(id); //Create a new VertexShader

creates a local variable of type VertexShader on the stack that lives as long as you remain inside stack scope. And when calling

return vertexShader;

it returns a new copy of your stack scoped variable to the recipient that called your setup function and after that cleans up the stack by removing your local instance from memory. The same is true for your unordered map since it isnt a pointer, the insert function of the std map creates a copy of your object inside the memory std map manages.

The only chance you have here is to use either some kind of ref pointer that takes responsability for keeping your instance alive as long as there are references to it alive or use a raw pointer so you need to take responsability to delete it after you were done so memory could be released properly (destructor wont be called since delete). Otherwise you would have two different copies and in the most anyoing case (as yours occuring) it will happen that your class gest destructed and data you previously attached gets lost

Edited by Shaarigan

Share this post


Link to post
Share on other sites
2 hours ago, Shaarigan said:

it returns a new copy of your stack scoped variable to the recipient that called your setup function and after that cleans up the stack by removing your local instance from memory.

The copy is not guaranteed due to C++'s Return Value Optimization.

2 hours ago, Shaarigan said:

class gest destructed

The object or class instance gets destructed, not the class itself.

3 hours ago, noodleBowl said:

it returns a custom class called VertexShader

Idd.: An object or class instance is returned, not the class itself.

3 hours ago, noodleBowl said:

By doing vertexShaders[id] = vertexShader am I really just making a copy and then eventually another deconstructor for the VertexShader placed into the map will be called?

Yes, the copy constructor is used resulting in a copy of vertexShader.

Share this post


Link to post
Share on other sites
4 hours ago, noodleBowl said:

Also when using the method the destructor is called twice?

Take a look at the Return Value Optimization example on Wikipedia. It is possible that the copy assignment operator is invoked twice (which also implies that the destructor is invoked twice). Although, I really doubt that any C++11/14 compiler would use a copy assignment in the presence of move semantics?

Edited by matt77hias

Share this post


Link to post
Share on other sites
2 hours ago, Shaarigan said:

it returns a new copy of your stack scoped variable to the recipient that called your setup function and after that cleans up the stack by removing your local instance from memory. The same is true for your unordered map since it isnt a pointer, the insert function of the std map creates a copy of your object inside the memory std map manages.

The only chance you have here is to use either some kind of ref pointer that takes responsability for keeping your instance alive as long as there are references to it alive or use a raw pointer so you need to take responsability to delete it after you were done so memory could be released properly (destructor wont be called since delete). Otherwise you would have two different copies and in the most anyoing case (as yours occuring) it will happen that your class gest destructed and data you previously attached gets lost

Thats not the case in modern C++ (>11) anymore. With a proper move-constructor/assignment operator, instead of creating a copy it will move the data of the local "vertexShader" variable into the target on the callers side. If you define VertexShader as non-copyable (deleted copy- ctor/assignment-operator) then you can make sure this is happening all the time, even before the recent http://en.cppreference.com/w/cpp/language/copy_elision. (which is different to RVO; but probably won't happen if you assign to a global like in the example-code)

However, one place this code certainly does create a copy is this:

4 hours ago, noodleBowl said:

vertexShaders[id] = vertexShader;

So the code as shown still is flawed in this way. But, you could easily do:

VertexShader& ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile) //  returns reference
{
	ID3DBlob *shaderCode = compileShaderFromFile(fileName, entryPoint, targetProfile);

	D3D11_INPUT_ELEMENT_DESC inputElementDescription[] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
  
    VertexShader& vertexShader = vertexShaders.emplace(fileName, id).first->second; // construct the shader inside the map => might need to adjust the call at the end, not sure about the iterator-syntax

	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	return vertexShader;
}

 

Share this post


Link to post
Share on other sites
Just now, matt77hias said:

A reference as data element in the collection?

Eh, forget that part, I had a brainfart and assumed std::unique_ptr would reallocate elements as it grows. See my edit, thats how I would do it.

Still I don't quite get your question. What i indendet was:

std::unordered_map<LPCSTR, std::unique_ptr<VertexShader>> vertexShaders;

But since (I hope I've got it right now that) its safe to keep a reference to an element in an unordered_map, thats unnecessary.

Share this post


Link to post
Share on other sites

As a side note:

You can use a create a resource manager that constructs instances of a subclass of VertexShader (which should have a virtual destructor) by using variadic templates to pass the constructor arguments to the base class VertexShader, and that stores weak pointers to them. That way the resource manager is kind of notified on deletion.

Take a look at: SharedresourcePool in the DirectXTK.

Share this post


Link to post
Share on other sites
 

Let me see if I have this right

// VertexShader class
class VertexShader 
{
  public:
  LPCSTR id;
  VertexShader() {
  }
  VertexShader(LPCSTR id) {
    this->id = id;
  }
  ~VertexShader() {
  }
};

//Test method
VertexShader ShaderModule::test() {
  //Create a VertexShader instance in local scope called shaderLocal
  VertexShader shaderLocal("localScope");
  
  return shaderLocal; //Copy shaderLocal to send back with the return (Send_Back_Copy). shaderLocal's destructor is called
}

//Main method
void main() {
  //Call the test method
  VetexShader shade = shaderModule.test(); //Send_Back_Copy from return is copied and saved into shade. Sent_Back_Copy's destructor is called
}
//shade's destructor is called after main exits

Is that is what is happening? Or do I have this totally wrong?

Edited by noodleBowl

Share this post


Link to post
Share on other sites

Did you test to see whats happening? Eighter with a debugger or std::cout/printf-statements?

As has been pointed out, with full optimizations no copies or destructions should be happen here at all.

In any case:

8 minutes ago, noodleBowl said:

VetexShader shade = shaderModule.test(); //Send_Back_Copy from return is copied and saved into shade. Sent_Back_Copy's destructor is called

This is not happening, due to copy elision the temporary returned by "test()" is considered non-existant and instead the returned content is placed direclty into the "shade" variable. So this line should read:

VetexShader shade = shaderModule.test(); // no copy-ctor or destructor called at all here

 

Share this post


Link to post
Share on other sites

Try this:

#include <iostream>

struct Widget {
    
    Widget() : m_data(0) {
        std::cout << "Constructor" << std::endl;
    }
    Widget(const Widget &widget) : m_data(widget.m_data) {
        std::cout << "Copy Constructor" << std::endl; 
    }
    Widget(Widget &&widget) : m_data(widget.m_data) {
        std::cout << "Move Constructor" << std::endl; 
    }
    ~Widget() {
        std::cout << "Destructor" << std::endl; 
    }
    Widget &operator=(const Widget &widget) {
        std::cout << "Copy Assignment Operator" << std::endl; 
        m_data = widget.m_data;
        return *this;
    }
    Widget &operator=(Widget &&widget) {
        std::cout << "Move Assignment Operator" << std::endl; 
        m_data = widget.m_data;
        return *this;
    }
    
    int m_data;
};

const Widget Foobar() noexcept {
    std::cout << "Foobar" << std::endl; 
    const Widget widget;
    return widget;
}

int main() {
   std::cout << "Begin" << std::endl;
   Widget widget = Foobar();
   std::cout << "End" << std::endl;
}

Visual Studio C++ Web Compiler

Begin
Foobar
Constructor

Copy Constructor // presume Debug mode
End
Destructor

(GCC?) C++ Web Compiler

C++14:

Begin
Foobar
Constructor
End
Destructor

C++11:

Begin
Foobar
Constructor
End
Destructor

C++98 (without move semantics and noexcept):

Begin
Foobar
Constructor
End
Destructor

All due to RVO (Return Value Optimization) which already existed before introducing move semantics.

Edited by matt77hias

Share this post


Link to post
Share on other sites

Alright so I was able to go back and do some proper testing. And I got some questions

Now using my original code (VertexShader ShaderModule::createVertexShader) and placing std::out inside the constructors and destructors, I get this as the output

CREATED
Game constructor called
Vertex Shader constructor called. Id: VertexShader
Vertex Shader constructor called. No id
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader Id Changed! id: BEFORE-PIXEL-SHADER

I still do not understand why the destructor is called twice??

//======== Shader.h
class Shader
{
public:
	LPCSTR id;

protected:
	Shader();
	Shader(LPCSTR id);
	~Shader();
};

//======= Shader.cpp
Shader::Shader()
{
}

Shader::Shader(LPCSTR id)
{
	this->id = id;
}

Shader::~Shader()
{
	std::cout << "Shader destructor called..." << std::endl;
}

//============= VertexShader.h
class VertexShader : public Shader
{
public:
	VertexShader();
	VertexShader(LPCSTR id);
	~VertexShader();
	ID3D11VertexShader *shader;
	ID3D11InputLayout *inputLayout;
};

//======== VertexShader.cpp
VertexShader::VertexShader()
{
	std::cout << "Vertex Shader constructor called. No id" << std::endl;
}

VertexShader::VertexShader(LPCSTR id) : Shader(id)
{
	std::cout << "Vertex Shader constructor called. Id: " << id << std::endl;
}

VertexShader::~VertexShader()
{
	std::cout << "Vertex Shader destructor called. Id: " << id << std::endl;
}

//====== In Shader Module
VertexShader ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile)
{
  /*Compile the shader code and setup the input description*/

	VertexShader vertexShader(id); //First constructor call. Arg constructor where id is VertexShader
	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	vertexShaders[id] = vertexShader; //Second constructor called. No arg constructor. Because of copy into map?
	return vertexShader; //First destructor called. Assumed destructor of vertexShader as id is VertexShader. Base Shader destructor called too
}

//======== In Graphics System
void GraphicsSystem::initialize(HWND windowHandle)
{
	createDirectXSystem(windowHandle);
	createDirectXBackBuffer();
	createDirectXViewport(800, 600);
	
	shaderModule.initialize(device, deviceContext);
	rendererModule.initialize(device, deviceContext);

    //vShader is a private member of GraphicsSystem. Declared in GrpahicsSystem.cpp as: VertexShader vShader;
	vShader = shaderModule.createVertexShader(L"default.shader", "VertexShader", "VShader", "vs_4_0");
    //After the createVertexShader method runs and the VertexShader is returned Shader another destructor is hit?
  
	vShader.id = "BEFORE-PIXEL-SHADER";
	std::cout << "Vertex Shader Id Changed! id:" << vShader.id << std::endl;
	pShader = shaderModule.createPixelShader(L"default.shader", "PixelShader", "PShader", "ps_4_0");
}

 

Especially since the same ideology is used here and no double destructor occurs. Just like what you guys mentioned would happen

//Executed Output:
Constructor called. Id: localScope
Default constructor called. No id value.
Destructor called. Id: localScope
Complete!
Destructor called. Id: newId
Destructor called. Id: MapId
  

// VertexShader class
class VertexShader
{
public:
	LPCSTR id;
	VertexShader() {
		std::cout << "Default constructor called. No id value." << std::endl;
	}
	VertexShader(LPCSTR id) {
		this->id = id;
		std::cout << "Constructor called. Id: " << id << std::endl;
	}
	~VertexShader() {
		std::cout << "Destructor called. Id: " << id << std::endl;
	}
};

std::unordered_map<LPCSTR, VertexShader> shaderMap;

//Test method
VertexShader test() {
	//Create a VertexShader instance in local scope called shaderLocal
	VertexShader shaderLocal("localScope");
	shaderMap["MapId"] = shaderLocal;
	shaderMap["MapId"].id = "MapId";

	return shaderLocal; //Copy shaderLocal to send back with the return (Send_Back_Copy). shaderLocal's destructor is called
}

//Main method
void main() {
	//Call the test method
	VertexShader shade = test();
	//No destructor call happened?

	shade.id = "newId";
	std::cout << "Complete!" << std::endl;
}
//shade's destructor is called after main exits
//The VertexShader in the map has its destructor called

Both of the above were executed in debug mod and stepped through

 

When it comes including header files, if a class of the header file has a static member(s) and I have ShaderModule GraphicsSystem::shaderModule; in the implementation file. I am creating a instance right then and there aren't I?

//Inside GraphicsSystem.h
class GraphicsSystem {
public:
	GraphicsSystem();
	~GraphicsSystem();
	static ShaderModule shaderModule;

	/* Other methods and variables */

private:
	/* Other methods and variables */
};

//In implmentation file
ShaderModule GraphicsSystem::shaderModule; //Creaing instance here? See constructor being called

//Main program
#include "GraphicsSystem.h"

void main()
{
	std::cout << "Started the program..." <<std::endl;
}

 

Edited by noodleBowl

Share this post


Link to post
Share on other sites

Your code is only telling part of the story. Add the copy and move constructor, copy and move assignment operator as well. They will be equal to the default, but you can add some printing as well.

BTW: the content in your map is destructed as well in your second code listing.

Edited by matt77hias

Share this post


Link to post
Share on other sites
20 hours ago, matt77hias said:

Your code is only telling part of the story. Add the copy and move constructor, copy and move assignment operator as well

So I went back and added those parts

Output:

CREATED
Input Module constructor called!
Game constructor called!
(1) Vertex Shader constructor called. Id: VertexShader
(2) Vertex Shader constructor called. No arg constructor
(3) Vertex Shader copy assignment called. Id: VertexShader-COPY_ASSIGN | Other Id: VertexShader
(4) Vertex Shader move constructor called. Id: VertexShader-MOVE_CON | Other Id: VertexShader
(5) Vertex Shader destructor called. Id: VertexShader
    Shader destructor called...
(6) Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN | Other Id: VertexShader-MOVE_CON
(7) Vertex Shader destructor called. Id: VertexShader-MOVE_CON
    Shader destructor called...
    Vertex Shader Id Changed! id:BEFORE-PIXEL-SHADER

Code break down:

//In ShaderModule.cpp
VertexShader ShaderModule::createVertexShader(LPCWSTR fileName, LPCSTR id, LPCSTR entryPoint, LPCSTR targetProfile)
{
	/*Other shader code needed for compiling the shader and etc */

	//(1) Create a VertexShader instance using id: VertexShader
	VertexShader vertexShader(id);

	device->CreateVertexShader(shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), NULL, &vertexShader.shader);
	device->CreateInputLayout(inputElementDescription, 2, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize(), &vertexShader.inputLayout);
	shaderCode->Release();

	//(2) When vertexShaders[id], the operator[] triggers the creation of a VertexShader instance using the no arg constructor and its placed into the map
	//(3) the operator= triggers the copy assignment operator function of VertexShader. Where the variable vertexShader is the variable being copied. The copy replaces the value that was created in (2)
	vertexShaders[id] = vertexShader;

	//(4) On the return, a move constructor is called. I do not understand why? I assume its in preparation to return a copy of the variable vertexShader
	//(5) The destructor for the local scope variable vertexShader is called
	return vertexShader;
}

//In GraphicsSystem.cpp
void GraphicsSystem::initialize(HWND windowHandle)
{
	createDirectXSystem(windowHandle);
	createDirectXBackBuffer();
	createDirectXViewport(800, 600);	
	shaderModule.initialize(device, deviceContext);

	//Numbers 6 & 7 AFTER execution of the createVertexShader method. Once its returns
	//(6) the operator= triggers the move assignment operator function
	//(7) After (6) is completed, the destructor for the VertexShader instance the move constructor created is called
	vShader = shaderModule.createVertexShader(L"default.shader", "VertexShader", "VShader", "vs_4_0");

	vShader.id = "BEFORE-PIXEL-SHADER";
	std::cout << "Vertex Shader Id Changed! id:" << vShader.id << std::endl;
	pShader = shaderModule.createPixelShader(L"default.shader", "PixelShader", "PShader", "ps_4_0");
}

So for the most part it makes sense, but I do not understand the move constructor. Is that what just happens when you return an instance? A clone is made with the move constructor. What does a move constructor actually do / what's the purpose?

Edited by noodleBowl

Share this post


Link to post
Share on other sites
7 hours ago, noodleBowl said:

vertexShaders[id] = vertexShader;

As you noticed correctly, std::map::operator[] first creates a default Value if no Value is associated with the given Key. This results in unnecessary overhead (default constructor and copy assignment operator). Therefore, a better alternative is to use std::map::insert instead which will only invoke a move constructor.

Edited by matt77hias

Share this post


Link to post
Share on other sites
7 hours ago, noodleBowl said:

//(4) On the return, a move constructor is called. I do not understand why? I assume its in preparation to return a copy of the variable vertexShader //(5) The destructor for the local scope variable vertexShader is called

These two, (4) and (5), puzzle me as well.

(6) and (7) completely handle the passing of the returned VertexShader and the destruction of the local one.

(4) and (5) are basically no-ops in this context which just add another indirection by constructing some temporary out of the local variable and destructing that local variable. Maybe this is for correctness since you changed global state in your move constructor by writing to stdout? I find this strange since there is the Copy Elision optimization to avoid the unnecessary copying of objects.

7 hours ago, noodleBowl said:

A clone is made with the move constructor. What does a move constructor actually do / what's the purpose?

The move constructor does actually not clone something. If you clone a Widget, you have the original Widget and the clone of that Widget which both will "look" (depending on your implementation) the same while both still exist at the same time. A move constructor rather swaps the contents of the newly constructed Widget and the (rvalue) argument Widget (passed as rvalue reference). For more information: see this blog post for instance.

As a side note: To make things even more tricky, one uses the std::move method to cast to an rvalue reference: just a cast, not the actual movement of data.

Edited by matt77hias

Share this post


Link to post
Share on other sites
6 hours ago, matt77hias said:

These two, (4) and (5), puzzle me as well.

(6) and (7) completely handle the passing of the returned VertexShader and the destruction of the local one.

(4) and (5) are basically no-ops in this context which just add another indirection by constructing some temporary out of the local variable and destructing that local variable. Maybe this is for correctness since you changed global state in your move constructor by writing to stdout? I find this strange since there is the Copy Elision optimization to avoid the unnecessary copying of objects.

Contrary to what I originally hinted at and belived at the time, copy elision does not work on named temporaries. So without RVO, thats exactly what should be happening here.

Note that there's also a slight, but important misconception here: copy ellision is NOT an optimization, its a guarantee towards the compiler to entirely omit anything related to copying/moving a value. Thats means, under copy/move ellision, there doesn't even need to be a valid copy/move-ctor (can be deleted or inacessessible); and any compiler that assumes otherwise is broken.
Likewise, the difference between returning by value pre-c++ 11 and post-c++11 is important as well - with move-semantics, you can legitametly return a non-copyable class that has a valid move-ctor, while without move-semantics, you cannot do that; since RVO is an optional optimization that may or may not be present - thus a return-statement must be valid even without it present. With move-semantics, you can define classes as non-copyable and still return them by value; and you also have the guarantee to not have any copyies made i.e. in debug-builds.

Share this post


Link to post
Share on other sites
11 minutes ago, Juliean said:

Note that there's also a slight, but important misconception here: copy ellision is NOT an optimization, its a guarantee towards the compiler to entirely omit anything related to copying/moving a value.

Wikipedia defines it as an optimization (haven't had time to dig into the ISO book).

Furthermore, you define RVO as an optimization whereas (regular and named) RVO is one Copy Ellision technique.

Edited by matt77hias

Share this post


Link to post
Share on other sites
6 hours ago, matt77hias said:

(4) and (5) are basically no-ops in this context which just add another indirection by constructing some temporary out of the local variable and destructing that local variable. Maybe this is for correctness since you changed global state in your move constructor by writing to stdout? I find this strange since there is the Copy Elision optimization to avoid the unnecessary copying of objects.

[...] in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value.

Ok, the RVO is optional apparently. There is proposal for guaranteed Copy Ellision. This is also an interesting article about Copy Ellision.

@noodleBowl Are you using -fno-elide-constructors in Clang/GCC?

Edited by matt77hias

Share this post


Link to post
Share on other sites
6 minutes ago, matt77hias said:

Wikipedia defines it as an optimization (haven't had time to dig into the ISO book).

Appearently there are different meanings for copy ellision, you are right, the one I was refering to was the recent one, from the top of the cppreference-ink:
 

Quote

 

Under the following circumstances, the compilers are required to omit the copy- and move- construction of class objects even if the copy/move constructor and the destructor have observable side-effects. They need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:

  • In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object:

T x = T(T(T())); // only one call to default constructor of T, to initialize x
  • In a function call, if the operand of a return statement is a prvalue and the return type of the function is the same as the type of that prvalue.

T f() { return T{}; }
T x = f();         // only one call to default constructor of T, to initialize x
T* p = new T(f()); // only one call to default constructor of T, to initialize *p

Note: the rule above does not specify an optimization: C++17 core language specification of prvalues and temporaries is fundamentally different from that of the earlier C++ revisions: there is no longer a temporary to copy/move from.

 

 

9 minutes ago, matt77hias said:

Furthermore, you define RVO as an optimization whereas RVO is one Copy Ellision technique.

 

3 minutes ago, matt77hias said:

Ok, the RVO is optional apparently.

Seems you're right about that one, but yeah, as you see the compiler is only permitted to ignore the copy/move, while in the case of the new c++17 rule I was refering too, it is required to do so, making the following code legal (that was previously not):

struct NonCopyable
{
	NonCopyable()
    {
    }

    NonCopyable(const NonCopyable&) = delete;
    void operator=(const NonCopyable&) = delete;

    NonCopyable(NonCopyable&&) = delete;
    void operator=(NonCopyable&&) = delete;
}

NonCoypable factory(void)
{
    return NonCoypable(); // pre-c++17, this does not compile.
}

Its suprisingly hard to determine whats what here, but I think its important to realize that there are different things going on here conceptually.

Share this post


Link to post
Share on other sites

@matt77hias

I'm using visual studio 2017. My toolest is Visual Studio 2017 (v141) and the Windows SDK version is 10.0.14393.0

I'm just running the default project setup (empty project), where my build is x86 debug, so I assume there are no optimizations

I also when back and tested insert and emplace. Emplace and operator[] in terms of move/copy/etc look the same. Insert though seems to be doing a lot more work

Same code as the previous post, except the adding to the map part was changed to

//For insert method
std::pair<std::unordered_map<LPCSTR, VertexShader>::iterator, bool> result = vertexShaders.insert(std::pair<LPCSTR, VertexShader>(id, vertexShader));
if (result.second == false)
	std::cout << "key was already in use" <<std::endl;

//For emplace method
std::pair<std::unordered_map<LPCSTR, VertexShader>::iterator, bool> result = vertexShaders.emplace(id, vertexShader);
if (result.second == false)
	throw std::exception("key already used");

Output for when using insert:

CREATED
Input Module constructor called!
Shader Module constructor called!
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Graphics System constructor called!
Game constructor called!
Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader copy constructor called. Id: VertexShader-COPY-CON Other Id: VertexShader
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-COPY-CON-MOVE_CON Other Id: VertexShader-COPY-CON
Vertex Shader destructor called. Id: VertexShader-COPY-CON
Shader destructor called...
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-MOVE_CON Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN Other Id: VertexShader-MOVE_CON
Vertex Shader destructor called. Id: VertexShader-MOVE_CON
Shader destructor called...

Output for when using emplace

CREATED
Input Module constructor called!
Shader Module constructor called!
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Graphics System constructor called!
Game constructor called!
Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader copy constructor called. Id: VertexShader-COPY-CON Other Id: VertexShader
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-MOVE_CON Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN Other Id: VertexShader-MOVE_CON
Vertex Shader destructor called. Id: VertexShader-MOVE_CON
Shader destructor called...

Output for when using operator[]

CREATED
Input Module constructor called!
Shader Module constructor called!
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Graphics System constructor called!
Game constructor called!
Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Vertex Shader copy assignment called. Id: VertexShader-COPY_ASSIGN Other Id: VertexShader
Shader constructor called. No arg
Vertex Shader move constructor called. Id: VertexShader-MOVE_CON Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader
Shader destructor called...
Vertex Shader move assignment called. Id: VertexShader-MOVE_CON-MOVE_ASSIGN Other Id: VertexShader-MOVE_CON
Vertex Shader destructor called. Id: VertexShader-MOVE_CON
Shader destructor called...

 

Edited by noodleBowl

Share this post


Link to post
Share on other sites
6 hours ago, noodleBowl said:

I'm just running the default project setup (empty project), where my build is x86 debug, so I assume there are no optimizations

Maybe you're right and need a Release configuration. You can also try Project Properties > C/C++ > Language > C++ Language Standard > C++17 or C++latest to see if this makes any differences.

Share this post


Link to post
Share on other sites
20 hours ago, matt77hias said:

Maybe you're right and need a Release configuration. You can also try Project Properties > C/C++ > Language > C++ Language Standard > C++17 or C++latest to see if this makes any differences.

Looking at my project that option is left blank. Personally I only have std:c++14 or std:c++latest available in the drop down. Seeing as blank is really a poor description of what standard I'm using I dug around in the Microsoft docs and found what they put as the default language standard version. According to that page visual studio 2017 uses std:c++14 by default

Switching to std:c++latest it does not seem do anything different when comparing the output of the operator[]

Now when I switch to a Release build  (language standard is left blank) things get interesting because my output for creating my VertexShader becomes:

Shader Module initing...
Shader constructor called. id: VertexShader
Vertex Shader constructor called. Id: VertexShader
Shader constructor called. No arg
Vertex Shader constructor called. No arg constructor
Vertex Shader copy assignment called. Id: VertexShader-COPY_ASSIGN Other Id: VertexShader
Vertex Shader move assignment called. Id: VertexShader-MOVE_ASSIGN Other Id: VertexShader
Vertex Shader destructor called. Id: VertexShader

Which is way more cleaner then the previous debug outputs I have been posting, so it seems a release build will definitely do some heavy lifting

Other then that thanks everyone for helping me out. This was a much needed refresher

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Now

  • Advertisement
  • Similar Content

    • By stream775
      Hello!
                I wrote a simple bones system that renders a 3D model with bones using software vertex processing. The model is loaded perfectly, but I can't see any colors on it. For illustration, you can see the 3D lines list, the bones ( 32 bones ) are in correct position ( bind pose ).
       
      Now, here's the problem. When I try to render the mesh with transformations applied then I see this:
      As you can see the 3D lines are disappearing, I'm guessing the model is rendered, but the colors are not visible for whatever reason. I tried moving my camera around the line list, but all I can see is some lines disappearing due to the black color of vertices? I'm not loading any textures, am I suppose to load them?
      However, if I render the vertices without applying ANY bone transformations, then I can see it, but it's a mess, obviously. If you're wondering why it's red, I have set color of these vertices ( only half of them ) to red and the rest half is white.
      First of all, my apologies for the messy code, but here it is:
      I'm not sure if vertices are suppose to have weights in them for software vertex processing. I'm storing them in a container, so you don't see them here.
      #define CUSTOMFVF ( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE ) struct CUSTOMVERTEX { D3DXVECTOR3 Position; D3DXVECTOR3 Normal; DWORD Color; }; This is how I store the vertices in container and give them red and white color:
      This is how I create the device:
      For every frame:
      This is the UpdateSkinnedMesh method:
      I have debugged bone weights and bone indices. They are okay. Bone weights add up to 1.0f, so I'm really wondering why I can't see the model with colors on it?
    • By komires
      We are pleased to announce the release of Matali Physics 4.0, the fourth major version of Matali Physics engine.
      What is Matali Physics?
      Matali Physics is an advanced, multi-platform, high-performance 3d physics engine intended for games, virtual reality and physics-based simulations. Matali Physics and add-ons form physics environment which provides complex physical simulation and physics-based modeling of objects both real and imagined. The engine is available across multiple platforms:
              Android         *BSD         iOS         Linux         OS X         SteamOS         Windows 10 UAP/UWP         Windows 7/8/8.1/10         Windows XP/Vista What's new in version 4.0?
               One extended edition of Matali Physics engine          Support for Android 8.0 Oreo, iOS 11.x and macOS High Sierra (version 10.13.x) as well as support for the latest IDEs          Matali Render 3.0 add-on with physically-based rendering (PBR), screen space ambient occlusion (SSAO) and support for Vulkan API          Matali Games add-on  
      Main benefits of using Matali Physics:
              Stable, high-performance solution supplied together with the rich set of add-ons for all major mobile and desktop platforms (both 32 and 64 bit)         Advanced samples ready to use in your own games         New features on request         Dedicated technical support         Regular updates and fixes
      The engine history in a nutshell
      Matali Physics was built in 2009 as a dedicated solution for XNA. The first complete version of the engine was released in November 2010, and it was further developed to July 2014 forming multi-platform, fully manage solution for .NET and Mono. In the meantime, from October 2013 to July 2014, was introduced simultaneous support for C++. A significant change occurred in July 2014 together with the release of version 3.0. Managed version of the engine has been abandoned, and the engine was released solely with a new native core written entirely in modern C++. Currently the engine is intensively developed as an advanced, cross-platform, high-performance 3d physics solution.
       
      If you have questions related to the latest update or use of Matali Physics engine as a stable physics solution in your projects, please don't hesitate to contact us.

      View full story
    • By JoshKlint_34394
      Today we are pleased to announce the release of Leadwerks Game Engine 4.5.
      Version 4.5 introduces support for VR headsets including the HTC Vive, Oculus Rift, and all OSVR-based hardware, allowing developers to create both room-scale and seated VR experiences. The Leadwerks virtual reality command set is robust yet incredibly simple allowing you to easily convert your existing 3D games into VR titles. To help get you started the source code for our Asteroids3D game has been updated for VR and is now freely available in the Leadwerks Games Showcase.
      Leadwerks Game Engine is uniquely well-suited for VR because of its fast performance, ease of use, and the availability of C++ programming for demanding VR games. Several optimizations for VR have been made including combining the rendering of both eyes into a single culling step. The stability and accuracy of Newton Game Dynamics means we can have in-depth physics interactions in VR.

      A new VR game template has been added to provide common VR features including teleportation locomotion and the ability to pick up and interact with objects in the environment.
      Visual Studio 2017
      We've also upgraded Leadwerks Professional Edition to build with Visual Studio 2017 so you can take advantage of the very latest Visual Studio features. Instructions for upgrading C++ projects from version 4.4 to 4.5 are available here.
      Other Improvements
      Added fog settings in editor and into map file format. New joint scripts and enhancements. Updated to Steamworks 1.41 You can pick up Leadwerks Game Engine with a discount during the Steam Winter Sale.
      About Leadwerks Software
      Leadwerks Software was founded in 2006 to make game development easy and fun. The company launched Leadwerks Game Engine on Steam in January 2014 and has experienced steady growth, now with over 20,000 paid users.  Leadwerks Game Launcher was released as an early access title in September 2015, allowing developers to publish games to Steam Workshop with no submission fee.

      View full story
    • By khawk
      Urho3D 1.7 has been released. The release for the open source, cross-platform 2D/3D game engine includes a number of new features and bug fixes, including new IK support, AppleTV platform support, WebAssembly support, improved font rendering, better integration with Bullet and Box2D, renderer improvements, and more.
      Download the release and view the full changelog at https://urho3d.github.io/releases/2017/08/19/urho3d-1.7-release.html#changelog.
       

      View full story
    • By khawk
      GameDev.net member @Bleys has released a C++ library that may be useful for game developers.

      Called DynaMix, the library:
      You can access the repository at https://github.com/iboB/dynamix and documentation at https://ibob.github.io/dynamix/.
      Learn more from the thread:
      .

      View full story
  • Advertisement