Advertisement

Unnecessary C++ features?

Started by July 13, 2000 07:53 AM
70 comments, last by null_pointer 24 years, 5 months ago
casting a an int to a float takes time because of the conversions necessary to make an float value that means the same thing as an int, it should not however produce a temporary value (maybe it will in an unoptimized build, but not in an optimized one).
When casting pointers however, there is no need to make a new, temporary variable with the new type, the compiler simply treats the original pointer as if it hade the requested type:

float * float_ptr = what_ever;
int * int_ptr = (int*)float_ptr;

This would generate space for 2 variables, the float_ptr and int_ptr vars. In the cast the generated code simply copies float_ptr into int_ptr, because at the assemply/machine level you dont have pointers to different types, you simply have pointers to some kind of data.

And also, someone (I think it was milo) said that the function

    void foo(void){// do nothing];    


generated the following sequence of assembly instructions:

push ebp
mov ebp, esp
pop ebp
ret 0

If this is true in an optimized build, then I suggest that you find a compiler that does a better job of optimizing.
With VC++ 6.0 it becomes (as it should be):

ret 0

quote:
Aren't temporary variables created when you cast?


Sometimes, yes. But not always. It depends if a type conversion is needed, and how that conversion is done. Any (non-member function) pointer type to any other pointer type shouldn't produce a temporary, but it's up to the compiler. With MSVC I'm pretty sure pointer conversion doesn't use a temp.


quote:
At least that is how I understand the new casting syntax to mean, and my book seems to say the same thing. Maybe I'm just misunderstanding it.


Your not using the new style casts, your using the copy ctors of built in types (this probably amounts to the same when optimisation is enabled). The expression :

int i = int(1.2);

calls the copy constructor of an int with the value 1.2, which creates a temporary int and assigns it to I (again, using the copy ctor). This is kinda like doing:

std::string str = std::string("Hello");

The new casts are static_cast, const_cast, dynamic_cast and reinterperate_cast. So you could do:
    int i = static_cast<int>(1.2);    


It's basically the same as far as the compiled code goes, but I prefer the static_cast because it makes my intentions clearer especially for pointers.

Using "int i = int(1.2);" is normally called function style casting, and for a while it was the 'new' casting, the new casts where added to make casting safe and easier to notice in your code.


Edited by - Wilka on July 16, 2000 7:16:58 PM
Advertisement
I''m just curious, how does the STL ''map'' store its data??

(Same questions goes for the other STL types...)

quote:
I''m just curious, how does the STL ''map'' store its data??

(Same questions goes for the other STL types...)


It''s implementation-dependant. The allocator for each container decides how its data is stored. However, if you use the default allocator, then:
vector: single block of memory
list: each element in its own place, i.e. each element is "new"-ed (though implementations may snag a block of memory and use it as a single pool).
dequeue: small chunks of memory that are allocated/released when the head or tail of the queue passes the boundaries.
map/set/multimap/multiset: red-black trees.
quote:
The allocator for each container decides how its data is stored. However, if you use the default allocator...


The allocators don''t effect how the data is stored, no matter what allocator you use std::map will still be a red-black tree. The allocators are used to be the containers to get memory, once the container has the memory it uses it the same way as it does for every other allocator. Basically allocates allow you to change the memory model, not the data structure.

If you want a different data structure, you''ll need to use a different container. Such as SGI''s hash_map.
quote: Original post by Anonymous Poster

And also, someone (I think it was milo) said that the function

        void foo(void){// do nothing];        


generated the following sequence of assembly instructions:

push ebp
mov ebp, esp
pop ebp
ret 0

If this is true in an optimized build, then I suggest that you find a compiler that does a better job of optimizing.
With VC++ 6.0 it becomes (as it should be):

ret 0


What I said was this was Release build without optimization. The original poster was compiling with Debug and no optimizations (I think).

I also said that with optimization I figured that it should totally disappear, but you might be right that the compiler at least will make the function call and return. I would hope though that it would identify a null function and avoid the call. Maybe I''ll get bored and check it tonight.

Mike Roberts
aka milo
mlbobs@telocity.com

Advertisement
quote: Original post by Wilka

void func(int num){    if(num < 10)        return;	my_class_with_complicated_ctor anArray[100000];	//impl...}   


The complicated ctor could be creating a few thousands variables on the heap, so even if you only had of these classes it would still be better to declare a variable after any return tests. And the same for goes for assigning a variable, if you don't create a variable when you know the value you want it to have can construct it from that value (or values) instead of using the default ctor then assigning latter.

If your using the ctor to allocate slow resources (like a network connection, or a file) waiting until you need the variable before you create it makes a big difference:

void foo(int n){	if(n < 10)		return;	my_network_class network;	//impl...}   


Maybe you haven't heard, but C doesn't have constructors.

However, you do have a point about the "slow resources" stuff. I wouldn't do things this way however.

Edited by - foofightr on July 17, 2000 4:52:55 PM
To Anonymous Poster:
Ok, sorry, guess I didn''t read the post as carefully as I thought.

The compiler will always create the function, even if it is only reduced to a ret (because it doesn''t know if it will be called from external code), it might however remove calls to it.

If it is a static function it might even be totally removed, I''m not sure.

Wilka:

That''s kind of what I thought - in assembly, there are four types, right? byte, word, dword, ptr? I''m not sure - it''s been a while since I''ve looked over assembly.

BTW, thanks for replying!


Anoymous Poster:

The compiler should remove foo() altogether if it is never called or referenced in the code, unless of course you export it from the project (like a DLL). In C++, you cannot use functions that have been defined in other modules unless they have been prototyped in the current module. So I guess you could say that all C++ functions are static (in the C sense of the word) by default.



- null_pointer
Sabre Multimedia
null_pointer:

Infact all functions are extern by default and the compiler cannot know what functions has been prototyped in another source file. If two .obj files are linked and one calls a function expected to reside in the other, then you wouldn''t want a compiler that simply removed functions just because they only ret''ed, you would get strange linker errors. Therefor it must always create the function, if there is a chance that som external code will use it.

If you compile your sources to a .lib or a regular .obj, everything is exported by default (unless you declare them ''static''). It is only when creating dlls you have to explicitly export something (using __declspec(dllexport) or a.def file)

But as I said, a good compiler might remove calls to empty functions, even if it doesn''t remove the actual function.

This topic is closed to new replies.

Advertisement