Wednesday, May 6, 2015

Digest for comp.lang.c++@googlegroups.com - 17 updates in 7 topics

"Norman J. Goldstein" <normvcr@telus.net>: May 06 04:09PM -0700

I was thinking of which situations are better to use pimpl or
polymorhphism. Both options allow hiding the implementation, so that
the main class does not need to be recompiled, should the implementation
change.
 
Certainly, polymorphism is the C++ elegant approach, but does require a
static/global method to create new instances of the main class. Both
approaches require indirection to access the implementation, and require
storing an extra pointer to do that (polymorphism has the pointer to the
vtable).
 
The defining difference between the two approaches seems to be that with
pimpl, you can, at run time, switch to a different implementation while
keeping the same main class. I conclude that there is no point using
pimpl unless you want to do "hot" switching of implementations.
Other opinions about this?
 
Here is code that prints "hello" using both pimpl and polymorphism.
 
///////////////////////// PIMPL ////////////////////
class ImplPimpl;
class MainPimpl
{
public:
MainPimpl( void );
~MainPimpl();
 
void hello( void );
 
private:
ImplPimpl* impl_;
};
 
#include <iostream>
using namespace std;
 
class ImplPimpl
{
friend class MainPimpl;
 
void hello( void )
{ cout << "ImplPimpl hello" << endl; }
};
 
MainPimpl::MainPimpl( void ) :
impl_( new ImplPimpl )
{}
 
MainPimpl::~MainPimpl( void )
{
delete impl_;
impl_ = nullptr;
}
 
void MainPimpl::hello( void )
{impl_->hello(); }
 
////////////////// POLYMORPHISM //////////////////
class MainPoly
{
public:
virtual ~MainPoly(){};
virtual void hello( void ) = 0;
 
static
MainPoly* create( void );
};
 
class ImplPoly : public MainPoly
{
void hello( void )
{ cout << "ImplPoly hello" << endl; }
 
};
 
MainPoly* MainPoly::create( void )
{ return new ImplPoly; }
 
////////////////////////////////////////////////
#include <memory>
 
int main( int argc, char* argv[] )
{
unique_ptr< MainPimpl > pimpl( new MainPimpl );
pimpl->hello();
 
unique_ptr< MainPoly > poly( MainPoly::create() );
poly->hello();
 
return 0;
}// main
Juha Nieminen <nospam@thanks.invalid>: May 06 08:45AM


> Saying, »q is not null«, when in fact »q == 0« /is/ true, is something
> that can be understood by all those who can guess what the intended
> meaning is, but might not be true by a strict reading of the wording.
 
That's like saying that an int& is "null" if the int it references
happens to have the value 0.
 
The reference is not null. The value it points to might be.
 
--- news://freenews.netfront.net/ - complaints: news@netfront.net ---
drew@furrfu.invalid (Drew Lawson): May 06 06:27PM

In article <references-20150505172123@ram.dialup.fu-berlin.de>
> When asked for the difference between pointers and references
> some say that references cannot be null.
 
If you say so. What normally hear is some variation of "references
must be bound to an object."
 
 
--
Drew Lawson | It's not enough to be alive
| when your future's been deferred
legalize+jeeves@mail.xmission.com (Richard): May 06 06:55PM

[Please do not mail me a copy of your followup]
 
drew@furrfu.invalid (Drew Lawson) spake the secret code
>> some say that references cannot be null.
 
>If you say so. What normally hear is some variation of "references
>must be bound to an object."
 
Every time I've asked the question in an interview, noone knew the
answer. Rather than being pedantic about the form of their answer, I
would simply accept anything that indicated that they knew that you
don't need to check references to see if they refer to something.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
Melzzzzz <mel@zzzzz.com>: May 06 09:18PM +0200

On Wed, 6 May 2015 18:55:42 +0000 (UTC)
> answer. Rather than being pedantic about the form of their answer, I
> would simply accept anything that indicated that they knew that you
> don't need to check references to see if they refer to something.
 
How would you check reference too see if they refer to something?
Victor Bazarov <v.bazarov@comcast.invalid>: May 06 04:29PM -0400

On 5/6/2015 3:18 PM, Melzzzzz wrote:
>> would simply accept anything that indicated that they knew that you
>> don't need to check references to see if they refer to something.
 
> How would you check reference too see if they refer to something?
 
In good ol' times, when everybody simply ignored UB, checking a null
reference was done by comparing the address of the object with 0. Same
with 'this' ('coz sometimes it just was "necessary to call a member
function for a non-existent object"):
 
void foo(sometype &ref) {
if (&ref != NULL) ...
}
 
or
 
void sometype::foo() {
if (this != NULL) ...
 
How else? :-)
 
Of course, the biggest problem was uninitialized pointers (not null
ones) which in the worst case would point to some random place in valid
memory, and references that were initialized by indirection from such
pointers.
 
V
--
I do not respond to top-posted replies, please don't ask
legalize+jeeves@mail.xmission.com (Richard): May 06 09:25PM

[Please do not mail me a copy of your followup]
 
Melzzzzz <mel@zzzzz.com> spake the secret code
>> would simply accept anything that indicated that they knew that you
>> don't need to check references to see if they refer to something.
 
>How would you check reference too see if they refer to something?
 
You can't. That is what indicates that they understand the difference
between a reference and a pointer. If they think references are just
pointers with . syntax instead of -> syntax, it means they don't
understand references.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
Doug Mika <dougmmika@gmail.com>: May 06 09:40AM -0700

Well, I think I'm beginning to understand all of this, but there is one more thing that is peculiar. If in my map object instantiation I specify a type for the sort algorithm, then why don't I have to include the sort functor or sort function pointer in the constructor?
 
ie. In the following program the map object called "fourth" has in it's type template parameter the type for classcomp, but doesn't include an instance of classcomp in its constructor. Which sort algorithm will fourth use: the one originally provided by std::map or classcomp::operator()?
 
// constructing maps
#include <iostream>
#include <map>
 
bool fncomp (char lhs, char rhs) {return lhs<rhs;}
 
struct classcomp {
bool operator() (const char& lhs, const char& rhs) const
{return lhs<rhs;}
};
 
int main ()
{
std::map<char,int> first;
 
first['a']=10;
first['b']=30;
first['c']=50;
first['d']=70;
 
std::map<char,int> second (first.begin(),first.end());
 
std::map<char,int> third (second);
 
std::map<char,int,classcomp> fourth; // class as Compare
 
bool(*fn_pt)(char,char) = fncomp;
std::map<char,int,bool(*)(char,char)> fifth (fn_pt); // function pointer as Compare
 
return 0;
}
legalize+jeeves@mail.xmission.com (Richard): May 06 05:53PM

[Please do not mail me a copy of your followup]
 
Doug Mika <dougmmika@gmail.com> spake the secret code
>more thing that is peculiar. If in my map object instantiation I
>specify a type for the sort algorithm, then why don't I have to include
>the sort functor or sort function pointer in the constructor?
 
The answer is obtained by looking at the declarations for the
constructors of std::map:
<http://en.cppreference.com/w/cpp/container/map/map>
 
Every place where the constructor accepts an instance of the
comparison function, the default value for this argument is Compare(),
a default constructed instance of your comparison type supplied in the
template argument.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
Victor Bazarov <v.bazarov@comcast.invalid>: May 06 02:40PM -0400

On 5/6/2015 12:40 PM, Doug Mika wrote:
> Well, I think I'm beginning to understand all of this, but there is
one more thing that is peculiar. If in my map object instantiation I
specify a type for the sort algorithm, then why don't I have to include
the sort functor or sort function pointer in the constructor?
 
If you don't supply a particular function, what's going to happen?
 
Case 1: the comparator is a class with operator() defined. If you don't
supply an object, it will be default-constructed from that type. What
does it mean to default-construct an object? If all that type does is
to use its op(), i.e. if the object has no *state*, who cares? It's
still going to be fine, a default-constructed object will be called to
compare your objects (keys), i.e. an empty instance of the class will
work hard [by means of its operator() member function] to successfully
compare what needs to be compared. All is well.
 
Case 2: the comparator is a function [pointer]. If you don't supply any
specific function, it will be default-constructed from that type (a
pointer to a function). What does it mean to default-construct a
pointer? What value will it have? What *code* will be behind that
pointer? It will be a _null_ pointer, IOW, a non-existent function.
Can *that* be used to compare your objects? Not going to work.
 
That is why when you specify a class (with an overloaded op() member),
you're pretty much done, unless you need a special state, which you can
give it at the construction time (less frequent). But when you specify
that "my comparator is a function that takes two references to int and
returns a bool", the compiler is not going to invent that function for you.
 
> std::map<char,int,bool(*)(char,char)> fifth (fn_pt); // function pointer as Compare
 
> return 0;
> }
 
V
--
I do not respond to top-posted replies, please don't ask
Paavo Helde <myfirstname@osa.pri.ee>: May 06 01:58PM -0500

Doug Mika <dougmmika@gmail.com> wrote in
> one more thing that is peculiar. If in my map object instantiation I
> specify a type for the sort algorithm, then why don't I have to
> include the sort functor or sort function pointer in the constructor?
 
The constructor parameter has a default value, namely the default value
of the given comparator type. In the functor variant, this is the
default-constructed functor, which is OK for simple functors which do not
need any extra data. In the function pointer variant, the default value
would be a NULL pointer, which obviously would not be very useful. Thus
you need to pass the pointer explicitly (and in your code actually have
done so).
 
So, the functor style is more readable, more flexible, more optimizable
and provides some sensible defaults. IOW, it just fits better with the
templated STL code. I guess the function pointer variant is mainly
supported for smoother upgrade path for C programmers used to qsort().
 

> include an instance of classcomp in its constructor. Which sort
> algorithm will fourth use: the one originally provided by std::map or
> classcomp::operator()?
 
The latter. There is no algorithm "provided originally" by std::map,
because std::map is a template, not a class, and the default algorithm
(std::less) appears just as the default choice for some template
argument.
 
The compiler will generate the actual class code from this template,
based on the template arguments. As you have replaced the default
template argument std::less with your classcomp, then the generated class
code for this instantiation has no idea that std::less exists, not to
speak about using or calling it somehow.
 
Juha Nieminen <nospam@thanks.invalid>: May 06 08:41AM

> Besides, the rationales behind language design decisions are discussed
> in comp.std.c++. Consider posting there, if you are really interested
> and not just venting some kind of frustration, OK?
 
Dispersing conversation about the same topic (ie. C++) into several
groups doesn't help anybody. It's not like this group gets thousands
of posts every day and there's need to alleviate the flooding.
 
--- news://freenews.netfront.net/ - complaints: news@netfront.net ---
Victor Bazarov <v.bazarov@comcast.invalid>: May 06 09:05AM -0400

On 5/6/2015 4:41 AM, Juha Nieminen wrote:
 
> Dispersing conversation about the same topic (ie. C++) into several
> groups doesn't help anybody. It's not like this group gets thousands
> of posts every day and there's need to alleviate the flooding.
 
Welcome back! I've missed you. It's not the same newsgroup without you.
 
V
--
I do not respond to top-posted replies, please don't ask
Nobody <nobody@nowhere.invalid>: May 06 08:48AM +0100

On Tue, 05 May 2015 15:00:38 -0700, asetofsymbols wrote:
 
> Is it possible to define operator operator[][]?
> (and operator[][][] and operator[][][][])
 
No. But you can define operator[] such that its return type supports
subscripting (either a pointer or a class which itself implements operator[]).
 
Alternatively, you could use operator() for array acess, so a[i][j][k]
becomes a(i,j,k).
Juha Nieminen <nospam@thanks.invalid>: May 06 08:47AM


> T& operator[](int i)
> {}
> Seems the only that compile
 
To achieve that syntax, T must be something that itself supports
operator[]. It's perfectly possible, you just have to define it
more recursively.
 
--- news://freenews.netfront.net/ - complaints: news@netfront.net ---
ram@zedat.fu-berlin.de (Stefan Ram): May 06 12:23AM

>asetofsymbols@gmail.com writes:
>>What does it mean s{}?
>In this case, it means, "an instance of s".
 
In »struct s{};«, it means that the type is empty,
in »int s{};« it requests »s« to be value-initialized.
 
As an expression, it's everything but a »functional
notation«, yet it's called »functional notation type
conversion«.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: May 06 01:14AM +0100

On Tue, 05 May 2015 19:52:03 +0200
 
> If f might want to pass the reference on to some other function,
> perhaps
 
> g(std::forward<std::string>(s));
 
That is not how std::forward is intended to be used: std::forward is
expected to be given a deduced type as its template parameter. The
expression 'std::forward<std::string>(s)' would just convert the lvalue
's' to a rvalue - in other words, it would be identical in effect to
'std::move(s)', which is a shorter and more comprehensible way of
writing it, if that is what is actually intended.
 
With respect to the OP's question about whether binding a temporary to a
function argument comprising a value std::string parameter will prompt
copy elision similar to RVO, the answer is no. Copy elision of objects
with non-trivial copy constructors and/or non-trivial destructors is not
permitted in that case. RVO only applies to function return values.
 
Chris
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: