-
Re: try to stump me on C++
A question about templates is a valid one? If so, please, consider the following declaration:
Code:
template <typename T, int depth> class FunnyOne // depth > 0, always.
{
FunnyOne<T, depth - 1> vec;
T val;
public:
FunnyOne() : val(T(10)) {}
T Get() {
return vec.Get();
}
void Set(T val) {
vec.Set(this->val + val);
}
};
template <typename T> class FunnyOne<T, 1>
{
T val;
public:
FunnyOne() : val(T(10)) {}
T Get() {
return this->val;
}
void Set(T val) {
this->val += val;
}
};
Also this function declaration:
Code:
int GetFunnyValue(int n)
{
FunnyOne<int, n> funny;
funny.Set(0);
return funny.Get();
}
What will be the correct answer to this?
Code:
int answer = GetFunnyValue(10);
-
Re: try to stump me on C++
Quote:
Originally Posted by aljodav
Also this function declaration:
Code:
int GetFunnyValue(int n)
{
FunnyOne<int, n> funny;
funny.Set(0);
return funny.Get();
}
Only compile-time constants may be used as template parameters, so this won't compile.
Change that to:
Code:
template <int n>
int GetFunnyValue()
{
FunnyOne<int, n> funny;
funny.Set(0);
return funny.Get();
}
// ...
int answer = GetFunnyValue<10>();
... and I guess the answer would be 100.
-
Re: try to stump me on C++
Quote:
Originally Posted by Zaccheus
Code:
if(fooA() != fooB())
{
}
So simple that I'm speechless, but before that, let me say that I intend that one of the defines be used. ;)
-
Re: try to stump me on C++
Quote:
Originally Posted by Plasmator
Do you think you'd get the job for being a smart ***?
My deer :rolleyes: , I'd rather let such a job be taken by a smart *** as you :rolleyes: . Maybe, you don't like me but **** **** ;) .
-
Re: try to stump me on C++
Quote:
Originally Posted by Hermit
Change that to:
Code:
template <int n>
int GetFunnyValue()
{
FunnyOne<int, n> funny;
funny.Set(0);
return funny.Get();
}
Correct! But you may notice that I haven't used any vector (OMG! sorry again, I mean array!)
Quote:
Originally Posted by Hermit
... and I guess the answer would be 100.
You've just got a job! ;)
-
Re: try to stump me on C++
Quote:
Originally Posted by aljodav
Maybe, you don't like me but ... ;) .
Stay in line.
-
Re: try to stump me on C++
Quote:
Originally Posted by Ejaz
Stay in line.
I'm sorry, I should know ***********************? OMG, I'm definitely OFF THIS LINE!
-
Re: try to stump me on C++
Quote:
Originally Posted by aljodav
I'm sorry, I should know ************************** OMG, I'm definitely OFF THIS LINE!
No need to be sorry, I'm putting you to the place where you belong.
-
Re: try to stump me on C++
How do handle agression in interviews! Great derail! Discuss!! :D :thumb:
-
Re: try to stump me on C++
Quote:
Originally Posted by Zaccheus
How do handle agression in interviews! Great derail! Discuss!! :D :thumb:
Actually, handling agression can definately be a part of a job interview. Especially if you are going to be in a client (internal or external) facing role.
I have been on both sides (interviewee and interviewer) where "role playing" involved dealing with a very angry "customer".
Interviewing tech people for their "soft" skills is becoming more and more common.
-
Re: try to stump me on C++
Quote:
Originally Posted by Zaccheus
Mitsukai, I was talking about the following technique:
Code:
MyClass& MyClass::operator=(const MyClass& other)
{
// use copy constructor to create a copy.
// If an exception happens, then 'this will remain unchanged.
MyClass temp(other);
// Use a member function called swap to exchange the internal representations of 'this' and temp, just like std::vector<T>::swap does.
swap(temp); // Cannot throw exceptions!
return *this;
}
Ok, I can't resist the temptation to enliven the discussion about this technique for implementing the assignment operator.
Exception safe though it may be, IMHO it can be brutally inefficient. Not because of the call to swap() (that can usually be implemented very efficiently), but because creating the temporary object can force memory allocation that would in many cases be unnecessary for the assignment.
Just think, for example, if std::vector or std::string's assignment operators were implemented in this way. Normally, so long as the size of the RHS vector/string does not exceed the capacity of the LHS vector/string, no reallocation is necessary for the assignment. If implemented using this technique, however, then a new piece of memory would be allocated on every assignment, and an old one thrown away: that's an unnecessary call to each of new[] and delete[] on every assignment. Considering how expensive memory allocation can be, I'd advise caution when choosing to use this technique.
Feel free to disagree, this is just my two cents. :)
-
Re: try to stump me on C++
Quote:
Originally Posted by HighCommander4
Feel free to disagree, this is just my two cents. :)
Glad to. :D :D
For the mjority of objects, the guarantee that the state will always be 100% consistant overrides the memory concerns and even the time concerns.
If the object happens to be large, then implementing the "bulk" as an independant object eliminates this situation.
About the only place I would agree with your concerns is on (very small) embedded systems.
-
Re: try to stump me on C++
Quote:
Originally Posted by TheCPUWizard
If the object happens to be large, then implementing the "bulk" as an independant object eliminates this situation.
Out of curiosity, how so?
-
Re: try to stump me on C++
Quote:
Originally Posted by HighCommander4
Out of curiosity, how so?
Very Over Simplified
Code:
class Bulky
{
private:
BigData *m_Bulk;
static bool s_SuppressBulk;
public:
Bulky(Bulky const &src)
{
if (!s_SuppressBulk)
m_Bulk = src.m_Bulk;
}
Bulky operator =(Bulky const &src)
{
s_SuppressBulk = true;
Bulky temp(src);
s_SuppressBulk = false;
Swap(temp);
return *this;
}
}
-
Re: try to stump me on C++
Quote:
Originally Posted by TheCPUWizard
Very Over Simplified
Code:
class Bulky
{
private:
BigData *m_Bulk;
static bool s_SuppressBulk;
public:
Bulky(Bulky const &src)
{
if (!s_SuppressBulk)
m_Bulk = src.m_Bulk;
}
Bulky operator =(Bulky const &src)
{
s_SuppressBulk = true;
Bulky temp(src);
s_SuppressBulk = false;
Swap(temp);
return *this;
}
}
:eek:
But, assuming that all Swap() does is swap pointers, then this is now a shallow copy...
Am I missing something?
EDIT: Wait a minute, I misread your code. As it stands now, no copying is done at all! temp's m_Bulk will be uninitialized, and when Swap() is called, the LHS object will end up with an uninitialized m_Bulk member!
-
Re: try to stump me on C++
Quote:
Originally Posted by HighCommander4
:eek:
But, assuming that all Swap() does is swap pointers, then this is now a shallow copy...
Am I missing something?
The code is actually missing quite a bit, and was merely intended to illustrate a means where the "cost" of creation of the temporary has been measured to cause a significant application level overhead.
In the 10+ years that Ihave used exception safe assignment, I have had to implement a solution based on the above concept exactly twice. And in one of those cases the code was re-factored within weeks (during the next sprint) to eliminate the need by refining the object model.
-
Re: try to stump me on C++
Quote:
Originally Posted by TheCPUWizard
Very Over Simplified
Code:
class Bulky
{
private:
BigData *m_Bulk;
static bool s_SuppressBulk;
public:
Bulky(Bulky const &src)
{
if (!s_SuppressBulk)
m_Bulk = src.m_Bulk;
}
Bulky operator =(Bulky const &src)
{
s_SuppressBulk = true;
Bulky temp(src);
s_SuppressBulk = false;
Swap(temp);
return *this;
}
}
I'm afraid this over-simplification makes the point unclear. If s_SuppressBulk is true, then the copy constructor is basically a no-op and m_Bulk is left uinitialized. If s_SuppressBulk is false then the copy constructor performs a shallow copy. Are there other key statements being omitted from your implementation of the copy constructor and assignment operator? Can you elaborate on them? Otherwise I cannot imagine any definition for BigData and Swap that would make this code function correctly.
I would also also create a separate, private constructor rather than use a static flag to alter the behavior of the regular copy constructor. The static variable thing is a bit ugly and makes thread-safety more difficult than it has to be.
-
Re: try to stump me on C++
Quote:
Originally Posted by Zaccheus
"Is it at all possible to create a local function inside a function in C++? If so, how?"
Yes. You create a local , and override .
Why would you want to do this:
'Local' gives you encapsulation. If you modify the 'local function', you only have to re-test the encapsulating function.
Just my two bits,
Pablo.
-
Re: try to stump me on C++
Quote:
Originally Posted by HighCommander4
Just think, for example, if std::vector or std::string's assignment operators were implemented in this way. Normally, so long as the size of the RHS vector/string does not exceed the capacity of the LHS vector/string, no reallocation is necessary for the assignment. If implemented using this technique, however, then a new piece of memory would be allocated on every assignment, and an old one thrown away: that's an unnecessary call to each of new[] and delete[] on every assignment. Considering how expensive memory allocation can be, I'd advise caution when choosing to use this technique.
That's a very good point, one of many special cases where the copy/swap technique can be improved upon. For the bulk of classes, though, I think the copy/swap technique is a good default option.
-
Re: try to stump me on C++
Lots of thing to learn here! A very good thread ( except aljodav's posts :P )
Well, how about this one?
Suppose we have a template class
Code:
template <unsigned long N>
class Something
{
typedef ...... unsigned_type_according_to_n;
};
The question is : Make "unsigned_type_according_to_n" to be
- unsigned char, if 0 <= N < 256
- unsigned short, if 256 <= N < 65536
- unsigned long, if 65536 <= N
Sorry if too easy ^_^
-
Re: try to stump me on C++
LOL, muse1987, that's great.
I have no idea, honestly.
Though I suspect the use of template specialisation, I can't think of any way to do it without declaring over 65000 specialised classes. :sick:
-
Re: try to stump me on C++
Quote:
Originally Posted by Hermit
I'm afraid this over-simplification makes the point unclear. If s_SuppressBulk is true, then the copy constructor is basically a no-op and m_Bulk is left uinitialized. If s_SuppressBulk is false then the copy constructor performs a shallow copy. Are there other key statements being omitted from your implementation of the copy constructor and assignment operator? Can you elaborate on them? Otherwise I cannot imagine any definition for BigData and Swap that would make this code function correctly.
I would also also create a separate, private constructor rather than use a static flag to alter the behavior of the regular copy constructor. The static variable thing is a bit ugly and makes thread-safety more difficult than it has to be.
I am afraid I forgot the axiom:
Quote:
Make things as simple as possible, but no simpler
:o :o :o
Rather than code, let me just use words....
A conventional (full) implementation would do a deep copy for post the "temp" and within the "swap". For many cases this is not an issue.
The approach can be optimized so that the number of deep copies is reduced to one (leaving the "swap" to be just a shallow copy [the example code implemented the reverse :blush: ]
However, when there is significant overhead to making a deep copy, assignments (regardless of implementation) become expensive regardless of implementation. At best this type of optimization can only provide a <50% improvement (half the time). Refactoring can eliminate the expense entirely (or at least defer it by techniques such as COW).
If the assignment (and/or copy construction) is going to be so expensive that it will impact application performance, then it is usually a good idea to prevent these from being implicitly invoked in any case.
The choice to "munge" the copy constuctor (vs. a specialized constructor) is another issue. If the practice of using initializer lists for all (possible) construction activities is followed, you end up with code duplication at every constructor. This will cause an increase in maintenance costs and lower reliability. But I agree that it is not an "ideal" solution.
-
Re: try to stump me on C++
Quote:
Originally Posted by Zaccheus
LOL, muse1987, that's great.
I have no idea, honestly.
Though I suspect the use of template specialisation, I can't think of any way to do it without declaring over 65000 specialised classes. :sick:
Maybe some form of template meta-programming?
-
Re: try to stump me on C++
Quote:
Originally Posted by PABLO_ALCH
Yes. You create a local
, and override
.
Why would you want to do this:
'Local' gives you encapsulation. If you modify the 'local function', you only have to re-test the encapsulating function.
Just my two bits,
Pablo.
That would work, but is probably too obtuse for any real use.
The testing benefit you mention is not real. If you modify a file in any way, you MUST do a 100% retest of the code that is contained in that file. (You would be willing to risk $1,000,000 that you never had a case of "happy fingers" and made a completely unintentional edit at some other point in the file?).
-
Re: try to stump me on C++
Quote:
Originally Posted by TheCPUWizard
A conventional (full) implementation would do a deep copy for post the "temp" and within the "swap". For many cases this is not an issue.
The approach can be optimized so that the number of deep copies is reduced to one (leaving the "swap" to be just a shallow copy [the example code implemented the reverse :blush: ]
My concern was not having to make two deep copies. That would be simply ridiculous. swap() can almost always be implemented to simply swap the internal representations without doing any actual work.
My concern was the extra memory allocation/deallocation that your method would require. I'm not even talking about particularly large objects. On the contrary, the scenario where this inefficiency would be most pronounced would be the assignment of a large number of small objects. For each assignment, when the temporary object is constructed, a new block of memory will be allocated, and when the temporary object is destructed, the old block of memory that the LHS object used to have (that was swapped into the temporary object) will be freed.
If, as I said, we have a large number of assignments, we have a large number of allocations that would, in a "plain", copy-the-data, implementation, be unnecessary. Given that allocating a block of memory can take significantly longer than copying a small amount of data, this makes the method very inefficient.
Quote:
If the assignment (and/or copy construction) is going to be so expensive that it will impact application performance, then it is usually a good idea to prevent these from being implicitly invoked in any case.
Agreed. But sometimes you need to be explicitly making copies of objects. In this case, and especially if you have a large number of objects, it pays off for the assignment operation to be as efficient as possible.
Quote:
The choice to "munge" the copy constuctor (vs. a specialized constructor) is another issue. If the practice of using initializer lists for all (possible) construction activities is followed, you end up with code duplication at every constructor. This will cause an increase in maintenance costs and lower reliability. But I agree that it is not an "ideal" solution.
I'm not sure I understand what you mean here... what do you mean by "munging" the copy constructor?
-
Re: try to stump me on C++
Quote:
Originally Posted by muse1987
Suppose we have a template class
Code:
template <unsigned long N>
class Something
{
typedef ...... unsigned_type_according_to_n;
};
The question is : Make "unsigned_type_according_to_n" to be
- unsigned char, if 0 <= N < 256
- unsigned short, if 256 <= N < 65536
- unsigned long, if 65536 <= N
I've only given this a minute of thought, so it's likely broken (not tested):
Code:
enum{TYPE_UCHAR, TYPE_USHRT, TYPE_ULONG};
template<int> struct XTypeResolver;
template<> struct XTypeResolver<TYPE_UCHAR> {typedef unsigned char value_type;};
template<> struct XTypeResolver<TYPE_USHRT> {typedef unsigned short value_type;};
template<> struct XTypeResolver<TYPE_ULONG> {typedef unsigned long value_type;};
template<unsigned long N> class Foo
{
enum
{
VALUE_TYPE_INDEX = (N >= 0 && N < 256) ? TYPE_UCHAR :
(N >= 256 && N < 65536) ? TYPE_USHRT : TYPE_ULONG
};
//Expose this to the user
public: typedef typename XTypeResolver<VALUE_TYPE_INDEX>::value_type value_type;
};
What's your solution?
-
Re: try to stump me on C++
Quote:
Originally Posted by HighCommander4
My concern was not having to make two deep copies. That would be simply ridiculous. swap() can almost always be implemented to simply swap the internal representations without doing any actual work.
My concern was the extra memory allocation/deallocation that your method would require. I'm not even talking about particularly large objects. On the contrary, the scenario where this inefficiency would be most pronounced would be the assignment of a large number of small objects. For each assignment, when the temporary object is constructed, a new block of memory will be allocated, and when the temporary object is destructed, the old block of memory that the LHS object used to have (that was swapped into the temporary object) will be freed.
If, as I said, we have a large number of assignments, we have a large number of allocations that would, in a "plain", copy-the-data, implementation, be unnecessary. Given that allocating a block of memory can take significantly longer than copying a small amount of data, this makes the method very inefficient.
I am sure you are aware (but for the other readers), an object which has MANY member objects (not pointers or references) is allocated in one block. so your point may not apply to many instances.
Also local variable allocation typically takes place upon unter to a method, so if there is ANY allocation it is usually done for the maxiumum amount of local memory needed.
Therefore (if not optimized by ther compiler), the following two samples require the exact same amount of allocation overhead.
Code:
void f()
{
int i;
}
Code:
class X
{
}
class Y
{
X data[1000]
}
void f()
{
X biggie[1000];
}
[/quote]
Both examples will "increment" the stack pointer ONCE and decrement it ONCE upon entry/exit. Hardly an "Expensive" operation.
Quote:
Agreed. But sometimes you need to be explicitly making copies of objects. In this case, and especially if you have a large number of objects, it pays off for the assignment operation to be as efficient as possible.
Again, if you are going to be copying a large object (into an EXISTING instance!) you really have to look at the design.
Quote:
Not sure I understand what you mean here... what do you mean by "munging" the copy constructor?
Using an external condition to influence how the copy constructor behaves.
In conclusion (at least for this reply), nobody would accept a database which did not adhere to ACID, yet so many people readily accept this in a program without even thinking about the conseuqences.
When you implement an architecture that 100% guarentees that the state of the entire system will be consistant across every statement boundary (ie the statement will have completed sucessfully, or had no visible impact) the number of test cases required is drastically reduced.
Consider the following (bad code) example:
Code:
class X
{
X(int v) {}
X(X const & src)
{
if (m_V == something) {throw....}
}
}
class Y
{
X data[1000];
}
void f(X &x2)
{
X x1;
// code
x2 = x1;
}
There are now at least 999 additional test cases required since the result of calling f(...) has 1002 possible execution paths rather than exactly two.
-
Re: try to stump me on C++
Quote:
Originally Posted by TheCPUWizard
I am sure you are aware (but for the other readers), an object which has MANY member objects (not pointers or references) is allocated in one block. so your point may not apply to many instances.
Also local variable allocation typically takes place upon unter to a method, so if there is ANY allocation it is usually done for the maxiumum amount of local memory needed.
Therefore (if not optimized by ther compiler), the following two samples require the exact same amount of allocation overhead.
Code:
void f()
{
int i;
}
Code:
class X
{
}
class Y
{
X data[1000]
}
void f()
{
X biggie[1000];
}
Both examples will "increment" the stack pointer ONCE and decrement it ONCE upon entry/exit. Hardly an "Expensive" operation.
I'm sorry if I wasn't clear enough. I was speaking of an object that handles a piece of dynamic (heap) memory, such as your m_Bulk example.
In a case like that, the constructor of the temporary would allocate a piece of heap memory for the temporary, swap() would simply swap the m_Bulk pointers of the temporary and the LHS object, and the destructor of the temporary would now deallocate the original piece of memory that the LHS object used to hold.
In a conventional implementation (i.e. straight copying, no copy constructor call/temporaries), assuming that the size of the memory block handled by the RHS object does not exceed that handled by the LHS object, no allocation/reallocation would be necesary. Hence the significant overhead, as allocating/deallocating a piece of heap memory can take significantly longer than copying a small chunk of data.
Quote:
Again, if you are going to be copying a large object (into an EXISTING instance!) you really have to look at the design.
Point taken. Looking back at the examples in my own code that I was thinking of, it was in fact copy construction that was taking place, and not assignment.
-
Re: try to stump me on C++
Quote:
Originally Posted by Plasmator
I've only given this a minute of thought, so it's likely broken (not tested): ......
What's your solution?
Exactly.
-
Re: try to stump me on C++
Quote:
Originally Posted by muse1987
Exactly.
Very nice indeed!