Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Object initialization confusion

87 views
Skip to first unread message

JiiPee

unread,
Dec 13, 2015, 7:39:09 AM12/13/15
to
We have:

class Person
{
public:
Person(string firstName, string secondName);
void setFirstName(string firstName);
void setsecondName(string firstName);
...
data members....
};

Now, If I create an object and initialize, normally we do:

1)
Person driver("John", "Smith");

But we could also do:
2)
Person driver;
driver.setFirstName("John");
driver.setsecondName("Smith");

Now, I know everybody says 1) should be done. And I know the benefits...
but.. am I also right the 2) tells more accurately
which parameter you are setting? In 1) we cannot be sure (without
checking the documentation) that which one is the first name
and which one is the second name. So from the code 1) we cannot be sure
whether we should input John as a first argument or second.
Maybe the second name should be the first argument (one might think....
looking at the code)?
But in 2) its very clear that first name is set to John.
I think others have struggled with this same. So... what to do? Which
one is better? Do you agree that we have a proglem
in 1) that we cannot be sure by looking at the code only that John
should be passed as a first parameter?

Is there another way to initialize an object so that only looking at the
code its very clear that John is passed as a first name
(because in 1) that is not clear really)?

This is what i have been struggled many times.

JiiPee

unread,
Dec 13, 2015, 7:43:32 AM12/13/15
to
I know that using different types, like enums, helps here as you cannot
pass a Cat enum to a Dog enum. But the problem comes when we must use
the same type variables (like strings in the example). But creating
different new types just for making the initialization work is a bit too
much to ask, isnt it?

Paavo Helde

unread,
Dec 13, 2015, 8:53:15 AM12/13/15
to
JiiPee <n...@notvalid.com> wrote in news:pFdby.692969$vF.5...@fx44.am4:

> We have:
>
> class Person
> {
> public:
> Person(string firstName, string secondName);
> void setFirstName(string firstName);
> void setsecondName(string firstName);
> ...
> data members....
> };
>
> Now, If I create an object and initialize, normally we do:
>
> 1)
> Person driver("John", "Smith");
>
> But we could also do:
> 2)
> Person driver;
> driver.setFirstName("John");
> driver.setsecondName("Smith");

> Is there another way to initialize an object so that only looking at
the
> code its very clear that John is passed as a first name
> (because in 1) that is not clear really)?

About looking at the code: if I see "John" and "Smith" then for me it is
pretty clear which is which. If it isn't then one might use e.g.:

using FirstName = std::string;
using SecondName = std::string;

Person driver(FirstName("Malcolm"), SecondName("Robert"));

Actually it is more important to get these things correct when *writing*
the code. Fortunately, IDE-s have pretty much solved this problem
nowadays via parameter info tooltips.

Cheers
Paavo






JiiPee

unread,
Dec 13, 2015, 8:58:54 AM12/13/15
to
On 13/12/2015 13:53, Paavo Helde wrote:
> About looking at the code: if I see "John" and "Smith" then for me it is
> pretty clear which is which. If it isn't then one might use e.g.:
>
> using FirstName = std::string;
> using SecondName = std::string;
>
> Person driver(FirstName("Malcolm"), SecondName("Robert"));
>
> Actually it is more important to get these things correct when*writing*
> the code. Fortunately, IDE-s have pretty much solved this problem
> nowadays via parameter info tooltips.

Actually I did not mean that its clear what is the input value. I meant
that it goes to a correct place. LIke in your example if the class was
defined in another way (second name must be the first argument):

class Person
{
public:
Person(string secondName, string firstName);
void setFirstName(string firstName);
void setsecondName(string firstName);
...
data members....
};

and you do your:

Person driver(FirstName("Malcolm"), SecondName("Robert"));

that would go wrong, isnt it? The problem with class initialization
contructors is that you do not know where to place a certain
argument.... you cannot see it from the code.

So we do not know whether the first name should be the first argument or
the second, that is the problem.

JiiPee

unread,
Dec 13, 2015, 9:02:57 AM12/13/15
to
On 13/12/2015 13:53, Paavo Helde wrote:
> using FirstName = std::string;
> using SecondName = std::string;
>
> Person driver(FirstName("Malcolm"), SecondName("Robert"));

This is not my point (what you asnwer here) .... but even in your answer
it is not *forced* that we have to do like that - the compiler does not
give an error if you do:

Person driver(SecondName("Malcolm"), FirstName("Robert"));

So your solutions does not increase the safety of the code, i think.

But again, this is not my point (to rename something)... so please read my other post to see what was my initial proglem.


Wouter van Ooijen

unread,
Dec 13, 2015, 9:12:28 AM12/13/15
to
Op 13-Dec-15 om 1:38 PM schreef JiiPee:
IMO this is a real problem: two or more parameteres of the same type,
where the order is significant.

Using different types for the arguments is of course a solution, but it
doesn't help in your case.

There was a recent language proposal for 'named parameter association',
but AFAIK it was reject by the C++ comittee because 'it does not solve a
real problem'. I strongly disagree.

For functions you could include the order of the parameters in the name:

some_function_first_last( "John", "Smith" )

but that does not work for constructors.

You could make the constructor private, and provide a friend function
with an appropriate name.

My preference would probably to do that, but do it for a specific 'name'
class, because your person class probably needs more arguments than just
the name, and a name is worth being a class of its own. (There is more
to names than just the first and last name: things inbetween, the
Russian patronymic, etc.)

Wouter van Ooijen

JiiPee

unread,
Dec 13, 2015, 9:23:21 AM12/13/15
to
On 13/12/2015 14:12, Wouter van Ooijen wrote:
> IMO this is a real problem: two or more parameteres of the same type,
> where the order is significant.
>
> Using different types for the arguments is of course a solution, but
> it doesn't help in your case.
>
> There was a recent language proposal for 'named parameter
> association', but AFAIK it was reject by the C++ comittee because 'it
> does not solve a real problem'. I strongly disagree.

Yes, this sounds to me like a good new feature, really useful and I
would use it definitely. I like the enum class came with C++11, we need
something like that here.

I many times scratch my head when creating my classes... whether to use
a contructor or other ways. The old constructor way feels a bit unsafe
to me. Thats why I sometimes prefer to use struct arguments:

struct PersonDetails driverDetails;
driver.name = "John";
driver.surname = "Smith";

and then pass this to the object:

Person driver(driverDetails);

Is this a good idea? at least you can see what is happening there. But
obviously the problem is that I might forget to set the surname for
example. So that gives other problems, so its not a perfect solution
neither.

JiiPee

unread,
Dec 13, 2015, 9:26:55 AM12/13/15
to
On 13/12/2015 14:12, Wouter van Ooijen wrote:
> For functions you could include the order of the parameters in the name:
>
> some_function_first_last( "John", "Smith" )

Yes that is one trick, but I personally do not like long function names
though. Also now the function name is depending on the parameters, so
its not independent name. So, I think I would prefer to do this other
one what you said.... to create a new class for first and second name.

JiiPee

unread,
Dec 13, 2015, 9:32:28 AM12/13/15
to
On 13/12/2015 14:12, Wouter van Ooijen wrote:
> IMO this is a real problem: two or more parameteres of the same type,
> where the order is significant.

and obviously we have this same problem with normal functions when
passing argument to them.
But all programming languages have this same problem, so maybe we just
have to live with it :).

Wouter van Ooijen

unread,
Dec 13, 2015, 9:36:36 AM12/13/15
to
Op 13-Dec-15 om 3:32 PM schreef JiiPee:
Named parameter association (Ada, Python, probably others I am not
famliar with) nicely solves this.

Wouter

Barry Schwarz

unread,
Dec 13, 2015, 9:42:11 AM12/13/15
to
While you specifically address initialization in the definition of an
object, the same issue exists with any function call that takes more
than one argument. (For example, in stoi does the base come before or
after the index pointer?) Do you feel the need to address it that
situation also?

--
Remove del for email

Wouter van Ooijen

unread,
Dec 13, 2015, 9:45:27 AM12/13/15
to
Op 13-Dec-15 om 3:26 PM schreef JiiPee:
Note that *I* suggested to create a class for *name*, which moves the
problem to that class. (As I said, name probably deserves to be a class
in its own right.)

But you could also create distinct types for firtsname, secondname, etc.

IMO longer function names are not a real problem. If you had named
parameter association, you would put the same semantic information in
the parameter names, so it is the same amount of typing, and only a
little more work in reading (because the names and the parameters are no
nicely grouped as they would using named parameter association).

// alas, not possible in C++
auto x = name(
first = "Wouter",
prefix = "van",
surname = "Ooijen"
);

// my least bad alternative
auto x = name_first_suffix_surname (
"Wouter",
"van",
"Ooijen"
);

IIRC there is something in boost to so somthing that looks like named
parameter association, but like a lot of boost it might not be worth the
trouble if you woulod only use it occasionally.

Wouter

Wouter

Alf P. Steinbach

unread,
Dec 13, 2015, 9:45:45 AM12/13/15
to
On 12/13/2015 1:38 PM, JiiPee wrote:
> [snip]
> Now, If I create an object and initialize, normally we do:
>
> 1)
> Person driver("John", "Smith");
>
> But we could also do:
> 2)
> Person driver;
> driver.setFirstName("John");
> driver.setsecondName("Smith");
>
> Now, I know everybody says 1) should be done. And I know the benefits...
> but.. am I also right the 2) tells more accurately
> which parameter you are setting? In 1) we cannot be sure (without
> checking the documentation) that which one is the first name
> and which one is the second name.

You can name the arguments to a constructor in many ways.

One way is to use the FAQ's "named parameters idiom". Another way is to
use the Boost parameters library (but it's too complex for me, although
at one time I drooled when discovering the preprocessor hacks in there).
And a third way can be to simply name special argument types:

<code>
#include <string>
#include <utility> // std::move
using namespace std;

template< class Value, class Type_id_type >
class Box
{
private:
Value v_;
public:
auto ref() -> Value& { return v_; }
Box( Value v ): v_( move( v ) ) {}
};

using First_name = Box< wstring, struct First_name_id >;
using Last_name = Box< wstring, struct Last_name_id >;

class Person
{
private:
wstring first_name_;
wstring last_name_;

public:
auto first_name() const -> wstring { return first_name_; }
auto last_name() const -> wstring { return last_name_; }

void set( First_name value ) { first_name_ = move(
value.ref() ); }
void set( Last_name value ) { last_name_ = move(
value.ref() ); }

Person( First_name first_name, Last_name last_name )
: first_name_( move( first_name.ref() ) )
, last_name_( move( last_name.ref() ) )
{}
};

#include <iostream>
auto main() -> int
{
auto x = Person( First_name( L"Jii" ), Last_name( L"Pee" ) );
wcout << x.first_name() << " " << x.last_name() << "\n";
}
</code>

> [snip]
> This is what i have been struggled many times.

Well, I hope the above gave you some ideas! ;-)


Cheers & hth.,

- Alf


Wouter van Ooijen

unread,
Dec 13, 2015, 9:52:57 AM12/13/15
to
> // alas, not possible in C++
> auto x = name(
> first = "Wouter",
> prefix = "van",
> surname = "Ooijen"
> );

In case anyone wonders why I didn't simply use

auto x = name(
first = "Wouter",
surname = "van Ooijen"
);

In the Nethlands we consider the surname NOT to include the prefix. Wgen
sorted alphabetically for instance, I would be found under the O. Yet
when writting, my 'second name' is still 'van Ooijen'.

To make matter even more interesting, IIRC our Southern neigbours the
Flemish (half of Belgium) *do* consider the prefix part of the surname,
so they would sort me under the 'v'.

Wouter

Paavo Helde

unread,
Dec 13, 2015, 9:53:10 AM12/13/15
to
JiiPee <n...@notvalid.com> wrote in
news:lQeby.1121511$4I5.9...@fx36.am4:

> On 13/12/2015 13:53, Paavo Helde wrote:
>> About looking at the code: if I see "John" and "Smith" then for me it
>> is pretty clear which is which. If it isn't then one might use e.g.:
>>
>> using FirstName = std::string;
>> using SecondName = std::string;
>>
>> Person driver(FirstName("Malcolm"), SecondName("Robert"));
>>
>
> So we do not know whether the first name should be the first argument
> or the second, that is the problem.

Yes I guessed that much, but this was not what you asked in the first post.

As I mentioned, for me the IDE-s have solved the latter problem. When I
look at the code and do not know which argument should go first and which
the second then I just hover the mouse over the function name and it tells
me.

Cheers
Paavo

Paavo Helde

unread,
Dec 13, 2015, 10:11:22 AM12/13/15
to
Paavo Helde <myfir...@osa.pri.ee> wrote in
news:XnsA56FABBEBD14Am...@216.166.105.131:
A correction: as this thread is specifically about constructors, then it
seems for constructors it is not so simple as just hovering the mouse -
one needs to put the cursor inside the parens and press Ctrl-Shift-Space
(VS2013). Maybe there are ways to configure it to work more
automatically.

And yes, as mentioned by others it would be nice if C++ supported named
parameters. I guess a problem with that proposal is that in the
declaration of a function the names are not much more than short
documentation strings and can be different in redeclarations:

int f(int x, int y); // first declaration
int f(int y, int x); // a legal redeclaration (currently)

f(1,2); // no ambiguity
f(x => 1, y => 2); // ???



Cheers
Paavo

JiiPee

unread,
Dec 13, 2015, 10:39:02 AM12/13/15
to
On 13/12/2015 14:41, Barry Schwarz wrote:
> While you specifically address initialization in the definition of an
> object, the same issue exists with any function call that takes more
> than one argument. (For example, in stoi does the base come before or
> after the index pointer?) Do you feel the need to address it that
> situation also?

when i use those functions, i normally always check the documentation to
be sure about the order of the parameters

JiiPee

unread,
Dec 13, 2015, 10:43:23 AM12/13/15
to
On 13/12/2015 14:45, Alf P. Steinbach wrote:
> Well, I hope the above gave you some ideas! ;-)

that looks complex, but I ll have to look at that

Jorgen Grahn

unread,
Dec 13, 2015, 11:59:48 AM12/13/15
to
On Sun, 2015-12-13, JiiPee wrote:
> We have:
>
> class Person
> {
> public:
> Person(string firstName, string secondName);
> void setFirstName(string firstName);
> void setsecondName(string firstName);
> ...
> data members....
> };
>
> Now, If I create an object and initialize, normally we do:
>
> 1)
> Person driver("John", "Smith");
>
> But we could also do:
> 2)
> Person driver;
> driver.setFirstName("John");
> driver.setsecondName("Smith");
>
> Now, I know everybody says 1) should be done. And I know the benefits...

Here ...

> but.. am I also right the 2) tells more accurately
> which parameter you are setting? In 1) we cannot be sure (without
> checking the documentation) that which one is the first name
> and which one is the second name. So from the code 1) we cannot be sure
> whether we should input John as a first argument or second.
> Maybe the second name should be the first argument (one might think....
> looking at the code)?
> But in 2) its very clear that first name is set to John.
> I think others have struggled with this same. So... what to do? Which
> one is better?

You answered that above. (1).

> Do you agree that we have a proglem
> in 1) that we cannot be sure by looking at the code only that John
> should be passed as a first parameter?
>
> Is there another way to initialize an object so that only looking at the
> code its very clear that John is passed as a first name
> (because in 1) that is not clear really)?
>
> This is what i have been struggled many times.

When I end up in that situation, I try to do a reality check.

Does it really matter? For example, I bet in a real program, new
Persons are created in one place (because you read them from the user
interface, or some input file) and then never modified (not the name,
anyway). If you get the construction wrong, you'll notice as soon as
you run the program.

But if I find that people never change names at runtime, I probably
don't want to implement (2) which enables you to do just that.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Lőrinczy Zsigmond

unread,
Dec 13, 2015, 1:21:30 PM12/13/15
to
constructor is the right way, especially if you can use named arguments:

Person (given_name=>"Zsigmond", family_name=>"Lorinczy",
name_order=>eastern);

Person (given_name=>"Barack", family_name=>"Obama",
name_order=>western);

JiiPee

unread,
Dec 13, 2015, 1:39:41 PM12/13/15
to
Here they actually seem to give a lot of ways to do it...although they
do not seem all very practical:

https://marcoarena.wordpress.com/2014/12/16/bring-named-parameters-in-modern-cpp/

JiiPee

unread,
Dec 13, 2015, 1:43:34 PM12/13/15
to
yes, I would like this one to come on c++:

|createArray(length=10, capacity=20);

thats how it should be
|

Öö Tiib

unread,
Dec 13, 2015, 2:25:25 PM12/13/15
to
There are always some sort of special operations with any variable (add
title to "surname" or hide it, show a "length" with or without unit) or
constraint checks (does "name" start with capital letter, is "length"
positive value). Also some operations available to fundamental or library
types typically do not make sense (for example assigning "temperature"
to "length" or doing bitwise operations with either).

We can always make little classes that enwrap the fundamental or
library types for to add special operations and checks and remove
availability of redundant operations. Usage of such classes results with
bigger type-safety and clarity and with very little code generated. We
can avoid making non-explicit conversion constructors and conversion
operators and our code will be slightly more verbose but utterly
clear what is what.

class FirstName;
class Surname;

class Person
{
public:
Person(FirstName f, Surname s);
...
// data members....
};

...

Person driver(FirstName("John"), Surname("Smith"));

Other possibility is that we are ourselves keeping track of various
constraints and orders and using the fundamental or library types
directly. With good memory it is possible, but then we don't have your
problem anyway.

Person driver("John", "Smith");

Empty constructor + setters of single properties are bad thing since
those do not enforce compile-time that all mandatory properties are
set and if values of properties are interrelated then it may enforce
caller to use particular order of setters. So as result author has to
write and caller has to read even more documentation.

JiiPee

unread,
Dec 13, 2015, 2:30:34 PM12/13/15
to
On 13/12/2015 18:54, Stefan Ram wrote:
> JiiPee <n...@notvalid.com> writes:
>> yes, I would like this one to come on c++:
>> |createArray(length=10, capacity=20);
> #include <iostream>
> #include <ostream>
> #include <cmath>
>
> int main()
> { double x; double y;
> ::std::cout << ::std::pow( x = 2, y = 3 )<< '\n'; }
>
> 8
>

oh but this is not we are talking about. We are talking about:
we have a function:

void createArray(int length, int capacity);
and it is called:
createArray(length=10, capacity=20);

so this:
createArray(len=10, capacity=20);

would give a compiler error because the first must be
length


JiiPee

unread,
Dec 13, 2015, 2:34:39 PM12/13/15
to
On 13/12/2015 19:25, Öö Tiib wrote:
> class FirstName;
> class Surname;
>
> class Person
> {
> public:
> Person(FirstName f, Surname s);
> ...
> // data members....
> };
>
> ...
>
> Person driver(FirstName("John"), Surname("Smith"));

yes, this is one solution, and quite simple one as well. Just needs to
find a way to quickly make those classes :). But surely that is
possible, there are tools for that. If you have right tools, creating a
small class like that takes only 5 seconds

David Brown

unread,
Dec 13, 2015, 2:50:43 PM12/13/15
to
There is currently a proposal to add named parameters to C++17, and it
would simply be illegal to use them if there were contradictory
declarations in scope. (The proposed syntax is slightly different, "f(x
: 1, y : 2);", but the principle is the same.)

I would like compilers to have such legal but contradictory
redeclarations as a common warning - there is no reason for them except
to confuse people using the code.


David Brown

unread,
Dec 13, 2015, 3:16:04 PM12/13/15
to
The problem with this is that there is no distinction between FirstName
and SecondName - these need to be separate classes in order to make
everything safe:

class Person {
public:
class FirstName {
public:
FirstName(string firstName) :
fname(firstName)
{}
string fname;
};
class SecondName {
public:
SecondName(string secondName) :
sname(secondName)
{}
string sname;
};

Person(FirstName firstName, SecondName secondName) :
fname(firstName.fname), sname(secondName.sname)
{}
Person(SecondName secondName, FirstName firstName) :
PersonB(firstName, secondName)
{}
private:
string fname;
string sname;
};

void foo2(void) {
// This is an error
Person("John", "Smith");
}

void foo3(void) {
Person(Person::FirstName("John"), Person::SecondName("Smith"));
}

void foo4(void) {
Person(Person::SecondName("Smith"), Person::FirstName("John"));
}

Some templates (or at least some macros) could be used to make the
overhead of the extra classes smaller in the source code (the optimiser
should remove all run-time overhead), and "using" could make the calls
to Person() neater - but at least it will accept the parameters in any
order, and always be safe.


JiiPee

unread,
Dec 13, 2015, 5:18:14 PM12/13/15
to
On 13/12/2015 20:15, David Brown wrote:
> The problem with this is that there is no distinction between
> FirstName and SecondName - these need to be separate classes in order
> to make everything safe:


thanks, a good code example

Bo Persson

unread,
Dec 14, 2015, 11:00:52 AM12/14/15
to
Stefan tried to show you that your code already has a meaning in both C
and C++ - assigning and passing the value at the same time.

In your last example, a global

int len;

would remove the compiler error and assign 10 to len.


Bo Persson

StuartRedmann

unread,
Dec 22, 2015, 1:46:58 AM12/22/15
to
On 12/13/15, JiiPee wrote:
>>
>> and obviously we have this same problem with normal functions when
>> passing argument to them.
>> But all programming languages have this same problem, so maybe we just
>> have to live with it .


On 12/13.15, Wouter van Ooijen wrote:
> Named parameter association (Ada, Python, probably others I am not
> famliar with) nicely solves this.
>
> Wouter


C# has named parameter association, and C# was intended to be a
successor of C++ (although I have no source for this):

from http://www.dotnetperls.com/named-parameters:
using System;

class Program
{
static void Main()
{
// Call the Test method several times in different ways.
Test(name: "Perl", size: 5);
Test(name: "Dot", size: -1);
Test(6, "Net");
Test(7, name: "Google");
}

static void Test(int size, string name)
{
Console.WriteLine("Size = {0}, Name = {1}", size, name);
}
}

Regards,
Stuart

Alf P. Steinbach

unread,
Dec 22, 2015, 5:05:34 PM12/22/15
to
On 12/22/2015 7:46 AM, StuartRedmann wrote:
>
> [snip] C# was intended to be a
> successor of C++ (although I have no source for this):

Rather, C# was designed as the primary programming language for the .NET
platform, which apparently co-evolved to support that language.

Being designed primarily for a virtual machine, the .NET Common Language
Runtime, C# could never be a successor of C++.

Anders Hejlsberg seems to have drawn inspiration both from his work
creating Borland's Turbo Pascal, and creating Microsoft's Java
implementation J++. Early C# might be characterized as a shallow copy of
Java with some pieces of Turbo Pascal freshness and practicality added
here and there. At the time, around 2000, I recommended that students
start learning programming using C#, just as I'd earlier recommended
Turbo Pascal, as a language where they could be productive fast,
including doing GUI stuff in reasonably simple ways.

Cheers!, & Happy Christmas (coming up),

- Alf

David Brown

unread,
Dec 28, 2015, 7:15:42 AM12/28/15
to
On 22/12/15 07:46, StuartRedmann wrote:
> On 12/13/15, JiiPee wrote:
>>>
>>> and obviously we have this same problem with normal functions when
>>> passing argument to them.
>>> But all programming languages have this same problem, so maybe we just
>>> have to live with it .
>
>
> On 12/13.15, Wouter van Ooijen wrote:
>> Named parameter association (Ada, Python, probably others I am not
>> famliar with) nicely solves this.
>>
>> Wouter
>
>
> C# has named parameter association, and C# was intended to be a
> successor of C++ (although I have no source for this):
>

C# was created as MS's alternative to Java after MS fell out with Sun.
It was never meant to replace or succeed C++ (though MS encouraged
people to write their Windows programs in C# rather than C++, in order
to promote lock-in).


0 new messages