By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,538 Members | 875 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,538 IT Pros & Developers. It's quick & easy.

Style: preferring parentheses to '=' for initialization?

P: n/a
Lately I find myself increasingly preferring the practice of going
ever-so-slightly out of my way to avoid the use of the form of
initialization which uses the '=' symbol, on the grounds that it can be
mistaken too easily for assignment. I like the avoidance of mistakes.
I like even more the frequent, subtle reinforcement (for myself and my
colleagues) of the point that assignment and initialization are
entirely different operations.

However, there are counterpoints. First, I still fall into old habits
-- I don't consider this a major problem, since the worst that can
happen is that I am a little inconsistent (which is the case anyway,
and probably to greater degree, unless one is religious in the other
direction and always uses '='). Of more significant concern is
readability. Due to old habits, in some cases I find the code slightly
less readable under this practice, though I expect this to diminish
with time. I have to be concerned with my colleagues as well, though.

So, what do you all think? Does anyone else do this? How do you feel
about the readability issue? Any other considerations I've failed to
identify?

Luke

May 26 '06 #1
Share this Question
Share on Google+
49 Replies


P: n/a
Luke Meyers wrote:
Lately I find myself increasingly preferring the practice of going
ever-so-slightly out of my way to avoid the use of the form of
initialization which uses the '=' symbol, on the grounds that it can be
mistaken too easily for assignment. I like the avoidance of mistakes.
I like even more the frequent, subtle reinforcement (for myself and my
colleagues) of the point that assignment and initialization are
entirely different operations.

However, there are counterpoints. First, I still fall into old habits
-- I don't consider this a major problem, since the worst that can
happen is that I am a little inconsistent (which is the case anyway,
and probably to greater degree, unless one is religious in the other
direction and always uses '='). Of more significant concern is
readability. Due to old habits, in some cases I find the code slightly
less readable under this practice, though I expect this to diminish
with time. I have to be concerned with my colleagues as well, though.

So, what do you all think? Does anyone else do this? How do you feel
about the readability issue? Any other considerations I've failed to
identify?

Luke


One issue you need it be aware of (if you aren't already) is the weird
parsing issue you can run into when trying to construct an object with
an unnamed temporary. Consider the following example:

class A
{
public:
explicit A(int) {}
} ;

class B
{
public:
explicit B(const A &) {}
int f() { return 42 ; }
} ;
int main()
{
int x = 3 ;
B b(A(x)) ;

b.f() ; // Oops! b isn't a variable like you expected!
}
The line of interest is:
B b(A(x)) ;

You'd think this would create an unnamed temporary of type A, and pass
it to B's constructor. However, it turns out that when declaring a
function, you are allowed to put parenthesis around the argument names.
So C++ will actually interpret this line as:
B b(A x) ;

That is, the declaration of a function named b that returns an object of
type B and takes one parameter of type A. The particularly troublesome
part is that there is no syntax problem there, so you don't get an error
until you actually try to use b. This problem is avoided with the '='
syntax for initialization. Example:
B b = B(A(x)) ;

Another solution, if you still prefer to leave '=' out of your
initialization, is to introduce another set of parenthesis:
B b((A(x))) ;

--
Alan Johnson
May 26 '06 #2

P: n/a
Alan Johnson <al****@no.spam.stanford.edu> wrote:
Another solution, if you still prefer to leave '=' out of your
initialization, is to introduce another set of parenthesis:
B b((A(x))) ;


Ugh. Why not just make it explicit with a temporary intermediate:

A temp(x);
B b(temp);

Sure, it takes two lines instead of one, but it's easy to read and
completely unambiguous. Let the compiler worry about optimizing away the
temporary.
May 26 '06 #3

P: n/a
Roy Smith wrote:
Alan Johnson <al****@no.spam.stanford.edu> wrote:
Another solution, if you still prefer to leave '=' out of your
initialization, is to introduce another set of parenthesis:
B b((A(x))) ;


Ugh. Why not just make it explicit with a temporary intermediate:

A temp(x);
B b(temp);

Sure, it takes two lines instead of one, but it's easy to read and
completely unambiguous. Let the compiler worry about optimizing away
the temporary.


It's not a temporary, at least AFA the compiler is concerned. It is
a full-fledged object (with a very common name, mind you), introduced
into the same scope as the intented ('b') object. So, there is no
sense in trying to mask that by calling it "temporary". The term has
a very specific and well-defined meaning in C++.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #4

P: n/a
Alan Johnson <al****@no.spam.stanford.edu> writes:
One issue you need it be aware of (if you aren't already) is the weird
parsing issue you can run into when trying to construct an object with
an unnamed temporary.


Another one is a declaration in an if-statement:

if (int i (f ())) // syntax error
if (int i = f ()) // ok

I, however, still prefer the construction syntax despite some annoying
inconsistencies in the language.

-boris
--
Boris Kolpackov
Code Synthesis Tools CC
http://www.codesynthesis.com
Open-Source, Cross-Platform C++ XML Data Binding
May 26 '06 #5

P: n/a
Luke Meyers posted:
Lately I find myself increasingly preferring the practice of going
ever-so-slightly out of my way to avoid the use of the form of
initialization which uses the '=' symbol, on the grounds that it can be
mistaken too easily for assignment. I like the avoidance of mistakes.
I like even more the frequent, subtle reinforcement (for myself and my
colleagues) of the point that assignment and initialization are
entirely different operations.

Okey dokey... well here's how I do it:
If I'm dealing with a primitive type, then I just do the following:

unsigned k = 7;

It's natural, and it's "the way things were supposed to be".
If I'm dealing with a Fancy Class Type, then I use the parentheses method.
It makes more sense because you're calling a constructor like a function:

Integer k(7);
If I want to default-initialise an array or an aggregate, then I:
double array[50] = {};

SomePOD obj = {};

SomePod array2[5] = {};
If I want maximum flexibility when writing a template, I use a little
thingie called "DefaultInitBearer" which provides me with a universal way
of default initialising any kind of object.

There's something which you haven't taken into account. The following two
statements *aren't* equivalent:

Integer k = 7;
Integer k(7);
And I'll demonstrate by using the following code:
class Integer {
private:

Integer(const Integer &);
/* Can't copy-construct */

public:

Integer(int const x) {}
};
int main()
{
Integer a(7); /* Compiles without effort */

Integer b = 7; /* Won't compile */
}

There reason why the second one won't compile is because:

Integer b = 7;

is interpretted as:

Integer b = Integer(7);

which is interpretted as:

Integer b( Integer(7) );

Can you see the copy construction? You would be correct in thinking that
any compiler worth its salt would optimize away the copy-construction, BUT
the class in question STILL must have the ability to be copy constructed.
So... here's what I advocate:

Treat POD's and Fancy Class Types different... because they ARE different,
and initialise them respectively as follows:

int k = 5;

Integer k(5);
-Tomás
May 26 '06 #6

P: n/a
Tomás wrote:
[..]
If I'm dealing with a Fancy Class Type, then I use the parentheses
method. It makes more sense because you're calling a constructor
Please don't confuse newbies with improper terminology. You're not
"calling a constructor". You're constructing (and initialising)
an object.
[...]


V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #7

P: n/a
Victor Bazarov posted:
Tomás wrote:
[..]
If I'm dealing with a Fancy Class Type, then I use the parentheses
method. It makes more sense because you're calling a constructor


Please don't confuse newbies with improper terminology. You're not
"calling a constructor". You're constructing (and initialising)
an object.

I didn't think he was a newbie (he refers to his "colleagues" multiple
times). I figured he was an experienced C++ programmer who put his doctrine
book down for a few minutes to think for himself.

-Tomás
May 26 '06 #8

P: n/a
Tomás wrote:
Victor Bazarov posted:
Tomás wrote:
[..]
If I'm dealing with a Fancy Class Type, then I use the parentheses
method. It makes more sense because you're calling a constructor


Please don't confuse newbies with improper terminology. You're not
"calling a constructor". You're constructing (and initialising)
an object.

I didn't think he was a newbie (he refers to his "colleagues" multiple
times). I figured he was an experienced C++ programmer who put his
doctrine book down for a few minutes to think for himself.


I wasn't talking about the OP. Newbies read this newsgroup too, if you
didn't know. They will see your post and decide that it's possible to
"call a constructor". If you wanted to communicate to the OP exclusively,
use private e-mail.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #9

P: n/a
Victor Bazarov posted:
They will see your post and decide that it's
possible to "call a constructor".

I've never liked the word "newbie" -- it's patronising. When I was a
novice C++ programmer, I would have preferred to be referred to as a
"novice" or "beginner".
If any novice would like to call a constructor:
#include <string>
#include <iostream>

int main()
{

/* Allocate the memory */

unsigned char (&buffer)[ sizeof(std::string) ]
= *new unsigned char[1][ sizeof(buffer) ];
/* Call the constructor */

std::string &str = *new(buffer) std::string("Hello World!");
/* Use the object */

std::cout << str;
/* Call the destructor */
typedef std::string stringForDestructorCall;

str.~stringForDestructorCall();
/* Deallocate the memory */
delete [] &buffer;
}}
May 26 '06 #10

P: n/a
Tomás wrote:
Victor Bazarov posted:
They will see your post and decide that it's
possible to "call a constructor".

I've never liked the word "newbie" -- it's patronising. When I was a
novice C++ programmer, I would have preferred to be referred to as a
"novice" or "beginner".


So? What's your point? That I am patronising? Towards whom? Novices?
Beginners? Get back on topic.
If any novice would like to call a constructor:

[...more confusion...]


There is no need to crawl into a bottle here. What you posted is not
a call to a constructor. Period. The use of placement new does in fact
resolve into a constructor being invoked, but so does a call to a regular
'new' or an introduction of a temporary or simple object definition. Did
you intend to somehow isolate the placement new and give it some special
meaning WRT "calling a constructor"? Well, what you've accomplished is
spreading more confusion. Please refrain from that.

If you need a good explanation about why it's not possible to call
a constructor, please refer to news archives, that topic has been covered
extensively.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #11

P: n/a
Victor Bazarov posted:

Did you intend to somehow isolate the placement new and give it some

special meaning WRT "calling a constructor"?

Yes, exactly.

Your argument is about semantics, nothing more.

If I say that a door is "opened", rather than "open", then you may infer
from the nuance what you like... but there's no clear-cut distinction
between an "open door" and an "opened door" -- so there's room to speculate
on how they're similar, and how they're different.

So what nuance do you suppose there is between "calls a contructor" and
"constructs an object"?

If you don't want to say that "placement new" calls a constructor, then by
all means, go ahead -- you may say that it "contructs an object" or
"initialises an object", if you like.

However, I will continue to state that "placement new calls a
constructor"... Why? Because it makes sense.

We both seem to have a high enough command of English to come up with our
own terminology... so you can "construct an object" while I "call a
constructor".

-Tomás
May 26 '06 #12

P: n/a
Tomás wrote:
Victor Bazarov posted:

Did you intend to somehow isolate the placement new and give it some special meaning WRT "calling a constructor"?

Yes, exactly.


Are you not listening? How is 'placement new' different from, say,
a regular 'new', then, in this particular aspect? I am not talking
about allocation of memory. I am specifically talking about object
construction (and invoking a constructor if there is one).
Your argument is about semantics, nothing more.
Nothing more? Do you mean to say that semantics (meaning) is not
important? You seem to be tripping yourself here.
If I say that a door is "opened", rather than "open", then you may
infer from the nuance what you like... but there's no clear-cut
distinction between an "open door" and an "opened door" -- so there's
room to speculate on how they're similar, and how they're different.
Yes, there is. But I am not going to debate it with you [here]. You can
go to 'alt.usage.english' and ask there, and try to convince everybody
*there* that there is no difference.
So what nuance do you suppose there is between "calls a contructor"
and "constructs an object"?
Simple. It is possible to construct an object without "calling"
a constructor, if the object is of POD type, for example. It is not
possible to call a constructor in a program, only to do something so
that it gets invoked. There is a difference between "calling" and
"causing an invocation". If you don't understand it, there is nothing
to discuss. Go learn that difference first, then come back.
If you don't want to say that "placement new" calls a constructor,
then by all means, go ahead -- you may say that it "contructs an
object" or "initialises an object", if you like.

However, I will continue to state that "placement new calls a
constructor"... Why? Because it makes sense.
I think you've managed to confuse yourself here. Yes, placement new
calls a constructor. Somehow. Magically. My point, and I insist on
it, is that the programmer has no means to "call a constructor". The
constructor gets invoked, that's all.

A constructor is a member function without a name. It cannot be looked
up during regular name lookup. Hence it can't be called. See how the
Standard defines a function call and a member function call.
We both seem to have a high enough command of English to come up with
our own terminology... so you can "construct an object" while I "call
a constructor".


You say what you will, in your own home, talking to your SO. Here we
use the terms and concepts set forth by the language Standard. Do you
understand that? That's why I asked you to stop spreading confusion.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #13

P: n/a
Victor Bazarov wrote:
Tomás wrote: [snip]
If any novice would like to call a constructor:

[...more confusion...]

[snip]
If you need a good explanation about why it's not possible to call
a constructor, please refer to news archives, that topic has been covered
extensively.


Yes, and it appears to be a religious war in which you happen to be on one
side. The issue is far less clear cut than you imply. The standard itself
uses the terminology that a constructor "is called" (although it does not
say by whom). Sometimes, but not always, the phrase is qualified as "is
implicitly called".
Best

Kai-Uwe Bux


May 26 '06 #14

P: n/a
Victor Bazarov posted:

Did you intend to somehow isolate the placement new and give it some special meaning WRT "calling a constructor"?

Yes, exactly.


Are you not listening? How is 'placement new' different from, say,
a regular 'new', then, in this particular aspect? I am not talking
about allocation of memory. I am specifically talking about object
construction (and invoking a constructor if there is one).

There comes a point where you assume that everyone understands you enough
so that you don't have to state the obvious. For example if I give
someone the following code snippet:

int main()
{
string str;

cout << str;
}

If someone replies saying: "There's no such class as string, no such
object as cout", then I'll reply, "Use your brain and presume that I took
a shortcut and left out the necessary include's and using declarations".

I am well aware that "placement new" isn't as cut-and-dried as "oh, it
calls the constructor". Here's an example:

struct Monkey {
string s;
}

int main()
{
void * const p = malloc( sizeof(Monkey) );

new(p) Monkey;
}
But in the context of this conversation, it does bear such simplicity --
if you want to explicitly call a constructor then use "placement new".
Thus, I say that "placement new" calls the constructor.

Your argument is about semantics, nothing more.


Nothing more? Do you mean to say that semantics (meaning) is not
important? You seem to be tripping yourself here.

There's no doubt in my mind that we both know what we're talking about
with regard to the C++ Programming Language. We're just arguing over how
we have expressed our understanding -- thus I say it's to do with
"semantics".

If I say that a door is "opened", rather than "open", then you may
infer from the nuance what you like... but there's no clear-cut
distinction between an "open door" and an "opened door" -- so there's
room to speculate on how they're similar, and how they're different.


Yes, there is. But I am not going to debate it with you [here]. You

can go to 'alt.usage.english' and ask there, and try to convince everybody
*there* that there is no difference.

I've been on that newsgroup before... a perfect example of people who
can't see the woods for the trees. The trick to understanding language is
not thinking. (But that's not exactly topical here)

So what nuance do you suppose there is between "calls a contructor"
and "constructs an object"?


Simple. It is possible to construct an object without "calling"
a constructor, if the object is of POD type, for example.

Yes, but in the context of this conversation, only a brain amputee would
use placement new on a POD. (Leaving out template code of course).

It is not
possible to call a constructor in a program, only to do something so
that it gets invoked.

Again, semantics.

If you have a Fancy Class, and you use placement new on it, then its
constructor gets called. Fair enough if you want to say that this is
simply a consequence of placement new; but in my eyes, the programmer's
intent was to simply call a constructor, and thus, placement can be used
to call a constructor.

There is a difference between "calling" and
"causing an invocation".

I presume you're talking about a "call" always being explicit, and an
"invocation" sometimes being implicit:

class Invocation {};

class Call : public Invocation {};

I think you've managed to confuse yourself here. Yes, placement new
calls a constructor.

Which is the basis of my argument.

Somehow. Magically.

Indeed again you're correct -- but this does not proclude from the
programmer's intention:

Programmer intended to call the constructor, so programmer used placement
new.

My point, and I insist on
it, is that the programmer has no means to "call a constructor". The
constructor gets invoked, that's all.

You're using your own definition of "call" and "invoke". There's nothing
in my mind which probhibits a "call" from being implicit.

A constructor is a member function without a name.

That's entirely just a perspective, a point of view, a way of looking at
things. Many more people would say, "It's a member function whose name is
identical to that of the class."

It cannot be looked
up during regular name lookup. Hence it can't be called. See how the
Standard defines a function call and a member function call.
Does the Standard make a distinction between a "call" and an
"invocation"? If it does, I wasn't aware of it, but I'll be more than
happy to change my use of language from here on in if so.

We both seem to have a high enough command of English to come up with
our own terminology... so you can "construct an object" while I "call
a constructor".


You say what you will, in your own home, talking to your SO.

SO? Never seen that abbreviation before; could you explain it please?
Anyway, I speak Irish at home.

Here we
use the terms and concepts set forth by the language Standard. Do you
understand that? That's why I asked you to stop spreading confusion.

Oh... I think I finally understand the crux of this argument -- are you
basing your stance on the distinction between a "call" and an
"invocation"?
-Tomás
May 26 '06 #15

P: n/a
Kai-Uwe Bux wrote:
Victor Bazarov wrote:
Tomás wrote:

[snip]
If any novice would like to call a constructor:

[...more confusion...]

[snip]

If you need a good explanation about why it's not possible to call
a constructor, please refer to news archives, that topic has been covered
extensively.


Yes, and it appears to be a religious war in which you happen to be on one
side. The issue is far less clear cut than you imply. The standard itself
uses the terminology that a constructor "is called" (although it does not
say by whom). Sometimes, but not always, the phrase is qualified as "is
implicitly called".


I don't see this as contradicting Victor's point: a constructor can be
called but not *directly* by the user -- it can only be implicitly
invoked.

Cheers! --M

May 26 '06 #16

P: n/a
Kai-Uwe Bux wrote:
Victor Bazarov wrote:
Tomás wrote: [snip]
If any novice would like to call a constructor:

[...more confusion...]

[snip]

If you need a good explanation about why it's not possible to call
a constructor, please refer to news archives, that topic has been
covered extensively.


Yes, and it appears to be a religious war in which you happen to be
on one side.


Pardon me, but why did you use the term "religious"? What's religious
about it?

It would have to be a really compelling reason to change side in this
issue. Do you have a reason to state?
The issue is far less clear cut than you imply. The
standard itself uses the terminology that a constructor "is called"
(although it does not say by whom). Sometimes, but not always, the
phrase is qualified as "is implicitly called".


I am not going to ask you to take part in that alleged "war", but if
*you* cannot determine whose side to take after reading the Standard
[and probably the news for some time], then how could a newbie deduce
that simple thing that he/she cannot call a constructor but only cause
the constructor to "be called" (passive voice only)?

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #17

P: n/a
Victor Bazarov posted:
Kai-Uwe Bux wrote:
Victor Bazarov wrote:
Tomás wrote: [snip]
If any novice would like to call a constructor:

[...more confusion...]

[snip]

If you need a good explanation about why it's not possible to call
a constructor, please refer to news archives, that topic has been
covered extensively.


Yes, and it appears to be a religious war in which you happen to be
on one side.


Pardon me, but why did you use the term "religious"? What's religious
about it?

Just a figure of speech... if someone is overly pedantic or obsessive
about something then they're said to be "religious" about it. He may also
be suggesting that the argument is centered more on stubbornness to
change one's viewpoint, rather than logic.

It would have to be a really compelling reason to change side in this
issue. Do you have a reason to state?
The issue is far less clear cut than you imply. The
standard itself uses the terminology that a constructor "is called"
(although it does not say by whom). Sometimes, but not always, the
phrase is qualified as "is implicitly called".


I am not going to ask you to take part in that alleged "war", but if
*you* cannot determine whose side to take after reading the Standard
[and probably the news for some time], then how could a newbie deduce
that simple thing that he/she cannot call a constructor but only cause
the constructor to "be called" (passive voice only)?

At the end of the day, a novice can still have the following thought
patterns:

"Hmm... right I've allocated memory; oh but this is a Fancy Class
Type, so I need to call its constructor too... ah I know, I'll use
placement new to call the constructor".

Granted, "placement new" can do more than simply call a class's
constructor (e.g. it can call the constructor of member objects), but
nonetheless, programmers DO use it simply with the intention of calling a
constructor.

If someone said to me: "You can't call a constructor explicitly", then
I'd show them "placement new".

-Tomás
May 26 '06 #18

P: n/a
In article <e5**********@news-srv1.vanderbilt.edu>,
Boris Kolpackov <bo***@codesynthesis.com> wrote:
Alan Johnson <al****@no.spam.stanford.edu> writes:
One issue you need it be aware of (if you aren't already) is the weird
parsing issue you can run into when trying to construct an object with
an unnamed temporary.


Another one is a declaration in an if-statement:

if (int i (f ())) // syntax error
if (int i = f ()) // ok

I, however, still prefer the construction syntax despite some annoying
inconsistencies in the language.


Still another issue is with generic code:

template <class T>
void swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}

as coded above this requires T to have a *non-explicit* copy
constructor. But:

template <class T>
void swap(T& x, T& y)
{
T tmp(x);
x = y;
y = tmp;
}

will work with a T with an explicit copy constructor. (I'm still trying
to figure out why anyone would *want* an explicit copy constructor).

-Howard
May 26 '06 #19

P: n/a
Victor Bazarov wrote:
Kai-Uwe Bux wrote:
Yes, and it appears to be a religious war in which you happen to be
on one side.


Pardon me, but why did you use the term "religious"? What's religious
about it?


I think he just means a discussion that has raised strong convictions
(like where the curly braces ought to go but even moreso). I would
suggest, however, that Tomás is the one who first linked this
discussion with religious conviction with his derisive use of the word
"doctrine" (here and in other threads), which certainly has a
religious/dogmatic connotation that he intends to invoke.

Cheers! --M

May 26 '06 #20

P: n/a
Tomás wrote:
Victor Bazarov posted:
A constructor is a member function without a name.

That's entirely just a perspective, a point of view, a way of looking
at things. Many more people would say, "It's a member function whose
name is identical to that of the class."


"Many more"? Really? Too bad. They will all be wrong. See 12.1.
The very beginning.
It cannot be looked
up during regular name lookup. Hence it can't be called. See how
the Standard defines a function call and a member function call.


Does the Standard make a distinction between a "call" and an
"invocation"? If it does, I wasn't aware of it, but I'll be more than
happy to change my use of language from here on in if so.


The Standard makes a point of using passive voice every time "call"
and "constructor" happen to be close-by.
We both seem to have a high enough command of English to come up
with our own terminology... so you can "construct an object" while
I "call a constructor".


You say what you will, in your own home, talking to your SO.

SO? Never seen that abbreviation before; could you explain it please?
Anyway, I speak Irish at home.


Significant Other. Security Officer. Saddened Outcast. Salty
Omelette. Who cares? You know whom or what you're talking to when
you're at home.
[..] I think I finally understand the crux of this argument -- are

you basing your stance on the distinction between a "call" and an
"invocation"?


No. Open the Standard and read all there is about constructors.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #21

P: n/a
Tomás wrote:
[..] He may
also be suggesting that the argument is centered more on stubbornness
to change one's viewpoint, rather than logic.
Very likely.
[..]
If someone said to me: "You can't call a constructor explicitly", then
I'd show them "placement new".


Yes. Exactly as I feared. No hope here.
May 26 '06 #22

P: n/a
mlimber wrote:
Kai-Uwe Bux wrote:
Victor Bazarov wrote:
> Tomás wrote:

[snip]
>> If any novice would like to call a constructor:
>>
>> [...more confusion...]

[snip]
>
> If you need a good explanation about why it's not possible to call
> a constructor, please refer to news archives, that topic has been
> covered extensively.


Yes, and it appears to be a religious war in which you happen to be on
one side. The issue is far less clear cut than you imply. The standard
itself uses the terminology that a constructor "is called" (although it
does not say by whom). Sometimes, but not always, the phrase is qualified
as "is implicitly called".


I don't see this as contradicting Victor's point: a constructor can be
called but not *directly* by the user -- it can only be implicitly
invoked.


From the standard [12.1/13]:

"Note: explicit constructor calls do not yield lvalues, see 3.10"

Thus, the standard knows something named "explicit constructor call". If you
want to say all *explicit* constructor calls are just *implicit*
invocations of constructors, so be it. I leave it for anybody to decide,
which terminological proposal is causing possible confusion.

All I wanted to point out is that the issue is not clear-cut; the standard
is very explicit about constructors not having names and thus not being
found through name lookup. However, I think it does not provide clear
enough a definition of the word "call" that would allow us to deduce from
the lack of names that constructors cannot be called. All that follows, as
far as I see, is that if a constructor was called (if at all possible),
this call would not qualify as a function call as described in [5.2.2].
However, that leaves the possibility that the standard knows two kinds of
calls: "constructor calls" and "function calls".
Best

Kai-Uwe Bux
May 26 '06 #23

P: n/a
Kai-Uwe Bux wrote:
mlimber wrote:
Kai-Uwe Bux wrote:
Victor Bazarov wrote:

Tomás wrote:
[snip]
> If any novice would like to call a constructor:
>
> [...more confusion...]
[snip]

If you need a good explanation about why it's not possible to call
a constructor, please refer to news archives, that topic has been
covered extensively.

Yes, and it appears to be a religious war in which you happen to be
on one side. The issue is far less clear cut than you imply. The
standard itself uses the terminology that a constructor "is called"
(although it does not say by whom). Sometimes, but not always, the
phrase is qualified as "is implicitly called".
I don't see this as contradicting Victor's point: a constructor can
be called but not *directly* by the user -- it can only be implicitly
invoked.


From the standard [12.1/13]:

"Note: explicit constructor calls do not yield lvalues, see 3.10"

Thus, the standard knows something named "explicit constructor call".
If you want to say all *explicit* constructor calls are just
*implicit* invocations of constructors, so be it. I leave it for
anybody to decide, which terminological proposal is causing possible
confusion.


Are you doing this on purpose? I wonder. The same paragraph, the first
"Note" says, ...(do you want to do the honours or should I?)... Never
mind. It says, "[Note: The syntax looks like an explicit call of the
constructor. ]" _LOOKS_LIKE_. Meaning: it is NOT. The other note, the
one you quoted, should not contain the text it contains. It's an apparent
defect. The paragraph talks about "A functional notation type conversion"
and not a call to a contructor.

Besides, those two things are "notes", and they are not normative, as I
am sure you're aware.

I am going to ask about it in comp.std.c++. Now.
[...]


V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #24

P: n/a
Victor Bazarov wrote:
Kai-Uwe Bux wrote:
Victor Bazarov wrote:
Tomás wrote: [snip]
If any novice would like to call a constructor:

[...more confusion...]

[snip]

If you need a good explanation about why it's not possible to call
a constructor, please refer to news archives, that topic has been
covered extensively.


Yes, and it appears to be a religious war in which you happen to be
on one side.


Pardon me, but why did you use the term "religious"? What's religious
about it?


I think the phrase "religious war" in the context of news groups has a
meaning that does not derive immediately from its components. What is going
on here is of course neither religious nor a war (I least, I would hope
so).

It would have to be a really compelling reason to change side in this
issue. Do you have a reason to state?


Yes; but I am sure, I cannot say anything that you have not heard before.
Best

Kai-Uwe Bux
May 26 '06 #25

P: n/a

mlimber wrote:
I don't see this as contradicting Victor's point: a constructor can be
called but not *directly* by the user -- it can only be implicitly
invoked.


What exactly is a call if it is not an invocation?

Invoke a procedure, call a procedure...the diff?

Invoke a constructor, call a constructor...again, the diff?

This "please protect the newbies" argument is even sillier than most.

May 26 '06 #26

P: n/a
Victor Bazarov wrote:
Kai-Uwe Bux wrote: [snip]
From the standard [12.1/13]:

"Note: explicit constructor calls do not yield lvalues, see 3.10"

Thus, the standard knows something named "explicit constructor call".
If you want to say all *explicit* constructor calls are just
*implicit* invocations of constructors, so be it. I leave it for
anybody to decide, which terminological proposal is causing possible
confusion.


Are you doing this on purpose? I wonder.


Of course:)
The same paragraph, the first
"Note" says, ...(do you want to do the honours or should I?)... Never
mind. It says, "[Note: The syntax looks like an explicit call of the
constructor. ]" _LOOKS_LIKE_. Meaning: it is NOT. The other note, the
one you quoted, should not contain the text it contains. It's an apparent
defect. The paragraph talks about "A functional notation type conversion"
and not a call to a contructor.
Ok, you could call that a defect. All I wanted to point out is that the
standard does not always follow your linguistic intuitions.

I can see your point: there are function calls and the standard goes on and
has lots of useful things to say about them. Now, one can make the proposal
to use "call" only when there is a function call to emphasize the
difference of functions and constructors; and I can see the merits of such
a proposal.

However, I do not see that the standard defines "call" (as opposed to
function call) that narrowly. If it does not then there is a possibility
that other calls exist in C++. What is your reason to assume that they
don't, given that the standard mentions at least "explicit constructor
calls"?

Or do you just want to say: those other calls *should* not exist and the
standard *should* be rewritten accordingly? (After all, rewriting
constructor calls out of existence would not change the observable behavior
of any C++ program).

Besides, those two things are "notes", and they are not normative, as I
am sure you're aware.


For what we are discussing, they don't have to be normative. Only stuff that
is needed to determine the observable behavior of a program or the
diagnostics required by a compiler needs to be normative. Which terminology
is appropriate for discussing C++ in news groups should be ruled by: which
terminology is useful to make heads and tails of the standard as a whole.
Best

Kai-Uwe Bux

May 26 '06 #27

P: n/a
mlimber posted:
...., which certainly has a
religious/dogmatic connotation that he intends to invoke.

Or is that "call"?
: )
-Tomás
May 26 '06 #28

P: n/a
Noah Roberts wrote:
mlimber wrote:
I don't see this as contradicting Victor's point: a constructor can be
called but not *directly* by the user -- it can only be implicitly
invoked.
What exactly is a call if it is not an invocation?

Invoke a procedure, call a procedure...the diff?

Invoke a constructor, call a constructor...again, the diff?


"Call" and "invoke" are synonymous. The difference we're talking about
is the ability to call something explicitly -- ordinary functions you
can, destructors you can, but constructors you cannot.
This "please protect the newbies" argument is even sillier than most.


Well, I agree that it's not really that important of a distinction in
most circumstances and thinking the wrong way won't lead too many
astray, but it remains a valid distinction nonetheless.

Cheers! --M

May 26 '06 #29

P: n/a
* Victor Bazarov:
Tomás wrote:
Victor Bazarov posted:
Tomás wrote:
[..]
If I'm dealing with a Fancy Class Type, then I use the parentheses
method. It makes more sense because you're calling a constructor
Please don't confuse newbies with improper terminology. You're not
"calling a constructor". You're constructing (and initialising)
an object.


I didn't think he was a newbie (he refers to his "colleagues" multiple
times). I figured he was an experienced C++ programmer who put his
doctrine book down for a few minutes to think for himself.


I wasn't talking about the OP. Newbies read this newsgroup too, if you
didn't know. They will see your post and decide that it's possible to
"call a constructor". If you wanted to communicate to the OP exclusively,
use private e-mail.


It is of course possible to call a constructor, explicitly or implicitly.

An explicit constructor call is not an lvalue, according to the standard.

A default constructor is a constructor that can be called without
arguments, according to the standard.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 26 '06 #30

P: n/a
Alf P. Steinbach posted:
It is of course possible to call a constructor, explicitly or
implicitly.

An explicit constructor call is not an lvalue, according to the
standard.

A default constructor is a constructor that can be called without
arguments, according to the standard.

This is great fun!
(1) Implicit:

int main()
{
std::string local_object;
}
(2) Explicit:
int main()
{
new( some_global_buffer ) std::string;
}
-Tomás
May 26 '06 #31

P: n/a
Alf P. Steinbach wrote:
[...]
It is of course possible to call a constructor, explicitly or
implicitly.
An explicit constructor call is not an lvalue, according to the
standard.


We'll see about that, after I get a reply in comp.std.c++.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #32

P: n/a
Victor Bazarov wrote:
We'll see about that, after I get a reply in comp.std.c++.


Didn't you forget to include a "Muhahahaha!" in there somewhere? ;-)

Cheers! --M

May 26 '06 #33

P: n/a
* Tomás:
Alf P. Steinbach posted:
It is of course possible to call a constructor, explicitly or
implicitly.

An explicit constructor call is not an lvalue, according to the
standard.

A default constructor is a constructor that can be called without
arguments, according to the standard.

This is great fun!
(1) Implicit:

int main()
{
std::string local_object;
}


Yep.

(2) Explicit:
int main()
{
new( some_global_buffer ) std::string;
}


If I understand what you mean correctly, namely placement new, then no,
that's not the explicit constructor call that isn't an lvalue.

Although any new expression for class type, whether placment or not, is
in a sense an explicit constructor call, and doesn't yield an lvalue.

string() is an explicit constructor call: the result is not an lvalue.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 26 '06 #34

P: n/a
mlimber wrote:
Victor Bazarov wrote:
We'll see about that, after I get a reply in comp.std.c++.


Didn't you forget to include a "Muhahahaha!" in there somewhere? ;-)


No, it's more like "grumble-grumble..."
May 26 '06 #35

P: n/a

Victor Bazarov wrote:
Are you doing this on purpose? I wonder. The same paragraph, the first
"Note" says, ...(do you want to do the honours or should I?)... Never
mind. It says, "[Note: The syntax looks like an explicit call of the
constructor. ]" _LOOKS_LIKE_. Meaning: it is NOT.


You are cheating here. That section is about functional notation type
conversions and that is what the note you are refering to is talking
about. The entire quote actually makes the meaning quite clear that
there is such a thing as explicit constructor calls but these two
examples are not such calls:

"A functional notation type conversion (5.2.3) can be used to create
new objects of its type. [Note: The syntax looks like an explicit call
of the constructor] [Example:

complex zz = complex(1,2.3);
cprint( complex(7.8,1.2) );

---end example] An object created in this way is unnamed. [Note: 12.2
describes the lifetime of temporary objects.] [Note: explicit
constructor calls do not yield lvalues, see 3.10]"

As you can see, what they are talking about in that section is indeed
not explicit constructor calls but the final note indicates that there
is such a thing and the first quote does not contradict that in any
way.

Finally, if that final note is a defect then where is the report?

May 26 '06 #36

P: n/a

Alf P. Steinbach wrote:
string() is an explicit constructor call: the result is not an lvalue.


Where would that be used though? 12.1.13 would seem to indicate most
uses I have seen of such syntax are not in fact explicit constructor
calls.

May 26 '06 #37

P: n/a
Noah Roberts wrote:
[..]
As you can see, what they are talking about in that section is indeed
not explicit constructor calls but the final note indicates that there
is such a thing and the first quote does not contradict that in any
way.
If I understand you correctly, the notes are intended to refer to some
other place in the Standard, where genuine, and existing for some other
purpose, explicit constructor calls are defined. Right? If you indeed
know about that other place in the Standard, are you going to keep it
a secret or are you going to share with us? Or am I misunderstanding
you here? Or are you just interpreting this paragraph _as_if_ that other
place in the Standard existed? Do tell.
Finally, if that final note is a defect then where is the report?


Nobody cared to submit a report on this yet, I am guessing. The absence
of a report is not a positive proof of the absence of a defect. I have
posted a message to comp.std.c++ asking about those two notes, as soon
as some conclusion is reached in the discussion that can insue, I'll
report back (for those who don't read c.s.c++).

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
May 26 '06 #38

P: n/a
* Noah Roberts:
Alf P. Steinbach wrote:
string() is an explicit constructor call: the result is not an lvalue.
Where would that be used though?


Assuming you're asking where would one use an explicit default
constructor call, e.g. like

foo( std::string() + "Akka bakka " + someCString + "bonka rakka!" );

12.1.13 would seem to indicate most
uses I have seen of such syntax are not in fact explicit constructor
calls.


12.1/13 is unfortunately inconsistent in what it implies, but
fortunately not in its literal interpretation.

Follow the link to 3.10, and it becomes clear that an "explicit
constructor call" in the 12.1/13 note refers to the "invocations of
constructors" part of the 3.10/2 note, where the full context is
"invocations of constructors or functions that return a class type".

Now of course an invocation of something doesn't return a class type,
but instead returns a class type object, and it's a bit unclear whether
it's really meaningful to say that a constructor call returns anything
(which is one possible way to parse the sentence), so this is a further
example that the standard isn't perfect, but is written under an
assumption that readers will apply common sense.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 26 '06 #39

P: n/a

Victor Bazarov wrote:
Noah Roberts wrote:
Alf already mentioned another place where the standard mentions calling
a constructor:

12.1.5

"A default constructor for class X is a constructor of class X that can
be called without an argument."

Another:

12.1.8

"Default constructors are called implicitly to create class objects of
static or automatic storage duration..."

Obviously the standard indicates that a constructor can be "called" so
the term is obviously correct.
Finally, if that final note is a defect then where is the report?


Nobody cared to submit a report on this yet, I am guessing. The absence
of a report is not a positive proof of the absence of a defect. I have
posted a message to comp.std.c++ asking about those two notes, as soon
as some conclusion is reached in the discussion that can insue, I'll
report back (for those who don't read c.s.c++).


After doing a search of the archives for that group (why didn't you
think of that?) I have to place my bets on a definative answer never
occuring. There are several places where members talk about explicitly
calling a constructor but no actual example that is given forth would
be convincing. For instance, several people describe placement new as
an explicit constructor call. Others indicate the following code:

class T {};

int main()
{

T();
};

This is an example of an explicit constructor call of type
T....creating an object that is never used. Others talk about the same
example in terms of it NOT being a constructor "call" or "invocation".

Yet another member asserts that static_cast is an explicit invocation
of the constructor:

(quote) What is the rationale behind having static_cast invoke constructors
even if marked "explicit"? I thought "explicit" was intended to say that a constructor does not
make sense as a conversion. But static_cast (or C style cast) is a
conversion that will use such a constructor anyway.


What "explicit" means is that the constructor should not be used unless
it is explicitly invoked. static_cast IS an explicit invocation of the
constructor.
(endquote)

So "static_cast<X>(y_inst)" would be considered an explicit call of the
constructor (assuming this conversion is possible). Others disagree of
course...

I don't think you are going to be able to say one way or the other. In
the end we are left with what the standard says when refering to
constructors and it repeatedly uses the term "call". Therefor anyone
else using the term is correct in doing so until that is changed in the
standard. Exactly what should be considered a call and what is not is
certainly highly debated.

May 26 '06 #40

P: n/a

Alf P. Steinbach wrote:
* Noah Roberts:
Alf P. Steinbach wrote:
string() is an explicit constructor call: the result is not an lvalue.


Where would that be used though?


Assuming you're asking where would one use an explicit default
constructor call, e.g. like

foo( std::string() + "Akka bakka " + someCString + "bonka rakka!" );


Ok, duh...I'm dumb.

May 26 '06 #41

P: n/a
Howard Hinnant wrote:
template <class T>
void swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}

as coded above this requires T to have a *non-explicit* copy
constructor. But:

template <class T>
void swap(T& x, T& y)
{
T tmp(x);
x = y;
y = tmp;
}

will work with a T with an explicit copy constructor. (I'm still trying
to figure out why anyone would *want* an explicit copy constructor).


To aggressively enforce their preference for the initialization style
under discussion, I suppose.

Luke

May 26 '06 #42

P: n/a
In article <SR******************@news.indigo.ie>,
No.Email@Address says...

[ ... ]
A constructor is a member function without a name.

That's entirely just a perspective, a point of view, a way of looking at
things. Many more people would say, "It's a member function whose name is
identical to that of the class."


The standard ($12.1/1) says: "Constructors do not have
names."

Victor's statement is clearly more than "just a
perspective" -- it's _fact_.

The "many more people" you mention would clearly just be
plain wrong.

This is NOT a distinction without a difference either --
it becomes quite important under some circumstances. For
one example, since a ctor does not have a name, it can
never be found during name lookup.
It cannot be looked
up during regular name lookup. Hence it can't be called. See how the
Standard defines a function call and a member function call.


Does the Standard make a distinction between a "call" and an
"invocation"? If it does, I wasn't aware of it, but I'll be more than
happy to change my use of language from here on in if so.


The standard doesn't seem to try to distinguish between
"call" and "invoke", but it very clearly distinguishes
between calling a function, and taking some action that
will (indirectly) call the function to be called.

For example, in discussing how ctors are called, the
standard says: "...an explicit type conversion using the
functional notation (5.2.3) will cause a constructor to
be called to initialize an object."
Oh... I think I finally understand the crux of this argument -- are you
basing your stance on the distinction between a "call" and an
"invocation"?


No -- on the difference between directly calling
something, and taking an action that indirectly causes it
to be called.

You don't call a constructor -- ever. You can't. It
doesn't have a name, so there's no way for its name to be
looked up, so there's no way for you to call it.

Your example using placement new did have a some point --
it's certainly true that placement new comes closer to
isolating the ctor call itself from other things, most
obviously allocating memory for the object.

Nonetheless, you're still not calling the ctor -- you're
still just taking an action that indirectly causes the
ctor to be called. In reality, the compiler often has to
generate extra "stuff" associated with doing construction
over and above a call to the ctor itself.

Consider one obvious example:

struct Y {
int val;

Y(int init) :val(init) {}
virtual ~Y() { std::cout << "~Y\n"; }
};

struct A {
A() {}
virtual ~A() {}
};

strut Z {
int val;
Z(int init) :val(init) { throw 1; }
};

class X : public Y {
A a;
Z z;
X(int a, int b) : Y(a), z(b) {}
};

Now, when you attempt to construct an object of class X,
even using placement new, the compiler has to do _quite_
a bit more than just call X's ctor. It has to arrange for
the ctors for A, Z, Y and X to be called (in the correct
order). Since Z's ctor turns out to throw an exception,
it also has to include code to catch the exception and
invoke the destructors for A and Y as well, and then re-
thrown the exception.

To summarize: no, you don't a ctor -- ever. There are a
number of things you can do that will cause ctors to be
called, but it's always done indirectly. Depending on the
classes involved, chances are that constructing an object
involves _quite_ a bit more than just calling its
constructor -- and while placement new removes one of the
other things that otherwise happens, it can still leave
quite a bit more than has to be done.

--
Later,
Jerry.

The universe is a figment of its own imagination.
May 27 '06 #43

P: n/a
* Jerry Coffin:

You don't call a constructor -- ever. You can't.
Wrong.

It doesn't have a name, so there's no way for its name to be
looked up,
Right.

so there's no way for you to call it.
Wrong.
.... Now, when you attempt to construct an object of class X,
even using placement new, the compiler has to do _quite_
a bit more than just call X's ctor.


Note that also for calls of virtual functions, more goes on than with a
non-virtual function.

We still call the calls calls.

Anyway, the argument is silly: it is a restriction of what something
undefined that you choose to call a "call" could be, to the point that
that undefined thing can't be anything relevant, and so, with a bit of
circular reasoning, the compiler has to do quite a bit more than that.

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 27 '06 #44

P: n/a
Alf P. Steinbach posted:
Assuming you're asking where would one use an explicit default
constructor call, e.g. like

foo( std::string() + "Akka bakka " + someCString + "bonka rakka!" );

But we all know that the following would be favourable:

foo( std::string("Akka bakka ") + someCString + "bonka rakka!" );
-Tomás
May 27 '06 #45

P: n/a
* Tomás:
Alf P. Steinbach posted:
Assuming you're asking where would one use an explicit default
constructor call, e.g. like

foo( std::string() + "Akka bakka " + someCString + "bonka rakka!" );

But we all know that the following would be favourable:

foo( std::string("Akka bakka ") + someCString + "bonka rakka!" );


Well no, I don't know that. ;-)

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 27 '06 #46

P: n/a
Alf P. Steinbach posted:
* Tomás:
Alf P. Steinbach posted:
Assuming you're asking where would one use an explicit default
constructor call, e.g. like

foo( std::string() + "Akka bakka " + someCString + "bonka rakka!" );

But we all know that the following would be favourable:

foo( std::string("Akka bakka ") + someCString + "bonka rakka!" );


Well no, I don't know that. ;-)

I just get this itch when I know something could be improved upon (even
when I know full well that the original poster knew what he/she was
doing!). I'm sure Victor would hate to see a "newbie" read your code and
then reproduce it. : )

-Tomás
May 27 '06 #47

P: n/a

Tomás wrote:
Alf P. Steinbach posted:
* Tomás:
Alf P. Steinbach posted:

Assuming you're asking where would one use an explicit default
constructor call, e.g. like

foo( std::string() + "Akka bakka " + someCString + "bonka rakka!" );
But we all know that the following would be favourable:

foo( std::string("Akka bakka ") + someCString + "bonka rakka!" );


Well no, I don't know that. ;-)

I just get this itch when I know something could be improved upon (even
when I know full well that the original poster knew what he/she was
doing!). I'm sure Victor would hate to see a "newbie" read your code and
then reproduce it. : )


I don't imagine there being a whole lot of difference between the two
versions. The have the same amount of temporaries, they call append
operation the same amount of times and with the same data...I just
really doubt there is any pragmatic difference between the two and if
there is it certainly isn't certain. For instance, a string could
easily be implemented in terms of:

string(const char * x) : whatever goes here to build a string... {
append(x); }

operator+(T t) { append(t); }

It couldn't just copy the pointer...if it where done that way, and that
is the most obvious, then it wouldn't matter if you did string() +
"xyz" or string("xyz").

May 27 '06 #48

P: n/a
Noah Roberts posted:

It couldn't just copy the pointer...if it where done that way, and
that is the most obvious, then it wouldn't matter if you did string()
+ "xyz" or string("xyz").

I'd advocate the style more than anything else, just as how I'd always
advocate:

SomeClass object(something);

instead of:

SomeClass object;
object = something;
Initialise wherever possible. If you can't, then assign.
-Tomás

May 27 '06 #49

P: n/a
* Tomás:
Noah Roberts posted:
It couldn't just copy the pointer...if it where done that way, and
that is the most obvious, then it wouldn't matter if you did string()
+ "xyz" or string("xyz").

I'd advocate the style more than anything else, just as how I'd always
advocate:

SomeClass object(something);

instead of:

SomeClass object;
object = something;
Initialise wherever possible. If you can't, then assign.


For string expressions it doesn't much matter.

Consider readability and, not the least, writeability:

throwX( STR __funcname__ + ": " + errorMessage );

throwX( std::string( __funcname__ ) + ": " + errorMessage );

I haven't actually done that, but now that I thought of it I think it
could be worth checking whether the OS API headers (which abound in
macros) define a macro STR: if not, then it could be very useful... :-)

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
May 27 '06 #50

This discussion thread is closed

Replies have been disabled for this discussion.