Which constructor?

S

Stefan Ram

I tried to modify the __gnu_debug string after #include
<debug/string> to get this information, but it did not work,
because I am having problems to print something from inside
of it using »::std::cerr«.

So, maybe someone here can tell me: What constructor or
method or whatever of the string class is called in the
case of each of the following two object definitions?

#include <string> /* ::std::string */

int main()
{ ::std::string s{ "alpha" };
::std::string t ={ "beta" }; }
 
V

Victor Bazarov

[...]
So, maybe someone here can tell me: What constructor or
method or whatever of the string class is called in the
case of each of the following two object definitions?

#include <string> /* ::std::string */

int main()
{ ::std::string s{ "alpha" };

This is direct initialization (construction), parameterized on char
const*, I suppose (if there is one with that argument).
::std::string t ={ "beta" }; }

This is a copy-initialization, preceded by the same parameterized one in
order to construct a temporary, I think. The compiler is still allowed
to forgo creation of the temporary, but the copy c-tor has to exist and
be accessible.

Are you seeing any problem constructing an std::string?

V
 
B

Barry Schwarz

Why don't you show us a small function that demonstrates the problem
you are having since any error is more likely to be in your code than
in the constructor.
 
S

Stefan Ram

Victor Bazarov said:
This is a copy-initialization, preceded by the same parameterized one in
order to construct a temporary, I think. The compiler is still allowed
to forgo creation of the temporary, but the copy c-tor has to exist and
be accessible.
Are you seeing any problem constructing an std::string?

I am writing about this in my German-language C++ tutorial,
and wanted to be sure that I explain it correctly. I would
have guessed that it was copy initialization, but I wanted
to be sure.

The best thing to be sure would be a source of
::std::string, and then insert debug print statements into it.
In fact, there is a GNU header:

namespace __gnu_debug
{
/// Class std::basic_string with safety/checking/debug instrumentation.
template<typename _CharT, typename _Traits = std::char_traits<_CharT>,
typename _Allocator = std::allocator<_CharT> >
class basic_string
...

which has constructors:

basic_string(const _CharT* __s, size_type __n,
const _Allocator& __a = _Allocator())
: _Base(__gnu_debug::__check_string(__s, __n), __n, __a)
{ }

and I tought I could just insert

::std::cerr << "this was just called\n";

into the braces. But the compiler complains

debug\string [Error] 'cerr' in namespace 'std' does not name a type

even after including ostream and iostream in this header and
trying dozen of other approaches.
 
V

Victor Bazarov

I am writing about this in my German-language C++ tutorial,
and wanted to be sure that I explain it correctly. I would
have guessed that it was copy initialization, but I wanted
to be sure.

The best thing to be sure would be a source of
::std::string, and then insert debug print statements into it.

You don't need to modify any of standard classes to illustrate your
point. Actually, many authors I've seen create their own classes that
look (or in part act) like standard ones, containers, functors, etc., to
show some specific behavior. And in your case I'd expect to see some
minimalistic implementation of, as you wish, a string simile. Generally
speaking, it might prove useful for some other mechanisms that you want
to illuminate, like dynamic memory management, move construction (and
assignment), etc.
In fact, there is a GNU header:

namespace __gnu_debug
{
/// Class std::basic_string with safety/checking/debug instrumentation.
template<typename _CharT, typename _Traits = std::char_traits<_CharT>,
typename _Allocator = std::allocator<_CharT> >
class basic_string
...

which has constructors:

basic_string(const _CharT* __s, size_type __n,
const _Allocator& __a = _Allocator())
: _Base(__gnu_debug::__check_string(__s, __n), __n, __a)
{ }

and I tought I could just insert

::std::cerr << "this was just called\n";

into the braces. But the compiler complains

debug\string [Error] 'cerr' in namespace 'std' does not name a type

even after including ostream and iostream in this header and
trying dozen of other approaches.

I have no comment on this, sorry. I think you're wasting your time with
those mechanisms. Stick to writing your own small and sweet classes to
serve as example. It can (and should) be inferred that standard classes
behave in the same manner.

V
 
S

Stefan Ram

Juha Nieminen said:
Why don't you replicate the constructors of std::string in your
own class, and try it? No need to guess.

Yes, possibly I want to do this. But I believe, what I
really wanted to do is to look up the place in n3797 where
the semantics of all the initializers are specified. It
should be somewhere in section 8.5. There is a
»braced-init-list« and then there is a »brace-or-equal-initializer«.
Then, there are wordings like:

»If the initializer is a (non-parenthesized)
braced-init-list, the object or reference is
list-initialized«

. »list-initialized«! Never heard that before, but I will
try to read more about it.
 
S

Stefan Ram

. »list-initialized«! Never heard that before, but I will
try to read more about it.

I wrote this example program:

#include <iostream> /* ::std::cout */
#include <ostream> /* << */
#include <string> /* ::std::string */
int main()
{ { ::std::string a( "alpha" ); ::std::cout << a << '\n'; }
{ ::std::string b = "beta"; ::std::cout << b << '\n'; }}

Is the following description correct?

In the case of

:std::string b = "beta";

, there will be a conversion operation to convert from »char
const *« to ::std::string, as if one had written

::std::string b = static_cast< ::std::string >( "beta" );

and then the temporary from the right-hand side will be
copied to the object b?

(But implementations can choose to optimize away some of
these steps, so that this copy-operation might not be
actually slower than the direct operation?)

Can we say that there is no general rule, but what actually
happens in direct-initialization versus copy-initialization
always depends on the specific class used?
(as in list-initialization versus non-list-initialization, too?)
 
V

Victor Bazarov

I wrote this example program:

#include <iostream> /* ::std::cout */
#include <ostream> /* << */
#include <string> /* ::std::string */
int main()
{ { ::std::string a( "alpha" ); ::std::cout << a << '\n'; }
{ ::std::string b = "beta"; ::std::cout << b << '\n'; }}

Is the following description correct?

In the case of

:std::string b = "beta";

, there will be a conversion operation to convert from »char
const *« to ::std::string, as if one had written

::std::string b = static_cast< ::std::string >( "beta" );

and then the temporary from the right-hand side will be
copied to the object b?

(But implementations can choose to optimize away some of
these steps, so that this copy-operation might not be
actually slower than the direct operation?)

That's how 8.5/16 describes it.
Can we say that there is no general rule, but what actually
happens in direct-initialization versus copy-initialization
always depends on the specific class used?
(as in list-initialization versus non-list-initialization, too?)

You mean whether to avoid copying? Probably. I tried this simple exercise:

class HasPrivateCopyCtor
{
HasPrivateCopyCtor(HasPrivateCopyCtor const&) {}
public:
HasPrivateCopyCtor(const char*) {}
};

int main()
{
HasPrivateCopyCtor ht1("one");
HasPrivateCopyCtor ht2 = "two";
}

and both VC++ 2012 and 2013 compiled it OK, even though the copying
cannot be accomplished since the copy c-tor is private. The online
trial compilation with g++ 4.8.1, however, gave me this error:

Compiling the source code....
$g++ main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
main.cpp: In function ‘int main()’:
main.cpp:3:4: error: ‘HasPrivateCopyCtor::HasPrivateCopyCtor(const
HasPrivateCopyCtor&)’ is private
HasPrivateCopyCtor(HasPrivateCopyCtor const&) {}
^
main.cpp:12:29: error: within this context
HasPrivateCopyCtor ht2 = "two";
^

which was exactly what I expected. YMMV, apparently.

V
 
S

Stefan Ram

Victor Bazarov said:
You mean whether to avoid copying?

At one point in the course I will be giving, I start to
teach to use objects of pre-defined classes, but I have not
yet explained how to define new classes or constructors. So
students do not yet know that there are constructors.

However, I can't hide from students that there are several
ways to define a variable of ::std::string type:

{ ::std::string s0;
::std::string s1{};
::std::string s2();
::std::string s3{ "abc" };
::std::string s4( "abc" );
::std::string s5 ={ "abc" };
::std::string s6 = "abc";
::std::string s7 =( "abc" );
... }

How should I explain the differences and how should I
decided on a recommendation of which syntax to use?

I plan to give names to the different possibilities and tell
the students that the meaning of the different definitions
can depend on the type of the definition. (That is: class A
might give the same meaning to a list-initializer as to a
non-list-initializer, while class B might ascribe different
meanings to them.)

Then, I will recommend to use the style of s3, because this
is intended to be the »universal initialization syntax«, but
tell them that sometimes other syntaxes have to be chosen as in:

auto v = 2;

or

::std::string s7( 4, 'x' );

. (Yes, s2 actually is a function.)

Comments on what might be wrong with the above are always
welcome!
 
V

Victor Bazarov

At one point in the course I will be giving, I start to
teach to use objects of pre-defined classes, but I have not
yet explained how to define new classes or constructors. So
students do not yet know that there are constructors.

However, I can't hide from students that there are several
ways to define a variable of ::std::string type:

{ ::std::string s0;
::std::string s1{};
::std::string s2();
::std::string s3{ "abc" };
::std::string s4( "abc" );
::std::string s5 ={ "abc" };
::std::string s6 = "abc";
::std::string s7 =( "abc" );
... }

How should I explain the differences and how should I
decided on a recommendation of which syntax to use?

Refer them to a good book. You can always tell them which is the way
you yourself prefer, and, hopefully, give them some explanation as to
why it's your preferred way, but they should make determination
themselves when they have enough information for that.

Also, tell them that their determination should undergo revisions
regularly (or at least once in a while) in order to avoid becoming dogmatic.
I plan to give names to the different possibilities and tell
the students that the meaning of the different definitions
can depend on the type of the definition. (That is: class A
might give the same meaning to a list-initializer as to a
non-list-initializer, while class B might ascribe different
meanings to them.)

They probably don't need to know, nor will they be able to retain that
knowledge from the first time they hear it from you. It's going to
sound too complex and too complicated, and the worst you can do is to
help develop an aversion to C++.
Then, I will recommend to use the style of s3, because this
is intended to be the »universal initialization syntax«, but
tell them that sometimes other syntaxes have to be chosen as in:

auto v = 2;

or

::std::string s7( 4, 'x' );

. (Yes, s2 actually is a function.)

Don't play games with them about it. Not in the beginning.
Comments on what might be wrong with the above are always
welcome!

Be simpler. Don't frighted them with all the complexity, ease them into
it. That's how I'd do it.

V
 
S

Stefan Ram

Victor Bazarov said:
Refer them to a good book.

I am going to give a course that is intended to teach (parts
of) C++. When I would tell them unconditionally to read a
»good book«, it might seem as if I would say that I am not
capable to teach them C++, and that they should read a book
instead of visiting my course.

However, I /do/ recommend good books at the end of the
course to read /after/ the end of the course, such as »The
C++ Programming Language«.
They probably don't need to know, nor will they be able to retain that
knowledge from the first time they hear it from you. It's going to
sound too complex and too complicated, and the worst you can do is to
help develop an aversion to C++.

The authors of n2640 also write something similar in n2640:

»Direct-initialization and copy-initialization« ... »are
"standardese" that we hope programmers should not concern
themselves with.«

But, I have to explain, why one can write

::std::string s( 3, 'c' );

but not

::std::string s = 3, 'c';

nor

::std::string s{ 3, 'c' };

in order to initialize s to "ccc".
Be simpler. Don't frighted them with all the complexity, ease them into
it. That's how I'd do it.

I would love to choose one single initialization syntax and
then only use this syntax. But this is not possible in C++.
 
S

Stefan Ram

Paavo Helde said:
Otherwise I agree with Victor, keep it simple. std::string has a public
copy ctor, so there is not much difference anyway. I would just teach s0
and s6 in the beginning and leave other stuff to some other more advanced
course.

The course I am planning now, indeed /is/ the (so-called)
»advanced« course. It was preceded by a »beginner's course«
with about 18 hours and no homework (all exercises are part
of these 18 hours).

The beginner's course (18 hours) had (list abbreviated here):

int; double; literals; char-const-*-literals; operators
(+;*;-;/); how to call functions from the standard
library; expressions; statements; how to define
variables and functions of primitive types like »int« or
»double«; additional operators like »%«; »<«; »?:« and
casts; additional types like »char«, »bool« and arrays;
control structures like »if« and »while«.

Now, I am starting to plan the »advanced course« (this
sometimes will be given in only 12 hours, other times, I
will have 24 hours). I want it to focus on »user-defined
types« (vulgo: object-oriented programming). The advanced
course is intended to start with these two parts:

1st part: using user-defined types that are pre-defined
in the standard library (as ::std::string or ::std::vector).

2nd part: defining new user-defined types

The introduction to various ways to write a definition of a
variable with class type (what I asked about in this thread)
is intended to take place at the beginning of the 1st part
of this »advanced« course.
 
S

Stuart

The course I am planning now, indeed /is/ the (so-called)
»advanced« course. It was preceded by a »beginner's course«
with about 18 hours and no homework (all exercises are part
of these 18 hours).

The beginner's course (18 hours) had (list abbreviated here):

int; double; literals; char-const-*-literals; operators
(+;*;-;/); how to call functions from the standard
library; expressions; statements; how to define
variables and functions of primitive types like »int« or
»double«; additional operators like »%«; »<«; »?:« and
casts; additional types like »char«, »bool« and arrays;
control structures like »if« and »while«.

Now, I am starting to plan the »advanced course« (this
sometimes will be given in only 12 hours, other times, I
will have 24 hours). I want it to focus on »user-defined
types« (vulgo: object-oriented programming). The advanced
course is intended to start with these two parts:

1st part: using user-defined types that are pre-defined
in the standard library (as ::std::string or ::std::vector).

2nd part: defining new user-defined types

The introduction to various ways to write a definition of a
variable with class type (what I asked about in this thread)
is intended to take place at the beginning of the 1st part
of this »advanced« course.

Your basics course looks like as if it was intended for someone who has
never programmed before (else it would not take 18 hours just to learn
the particular syntax of C++). Anyone who needs to be introduced to the
very basics will very likely need lot's of time to process this input.

And with that I don't mean till the next semester starts, but several
months or even years with actual programming (after all, you taught them
pretty much all there is to know about C). I think that it takes a very
long time to develop an "algorithmic" view on the world, and to realize
which problems can be solved quite easily and which take thousands of
man-years to complete. And, of course, some problems can't be solved at
all...

IMHO, it makes little sense to teach object-orientation to people who
never had written code before. The best you can expect is probably that
they can write some crappy exercise code, but it takes many years to get
to grips with object orientation (or better: how to divide the problem
space into components).

Of course, I can only speak from personal experience. I used to be a
tutor for an introductory programming course (Modula3, Java, Ada95).
This course encompassed 4 Semesterwochenstunden (sorry, don't know how
to translate this), and as soon as objects got introduced, almost
everybody was lost (except for those who were already able to write
programs before they attended this course).

This is probably due to the fact that the idea behind object orientation
becomes evident when you look at a problem space that is complicated
enough. Most courses use some convoluted problems a la "animal->eat()"
which don't make things easier to understand. Quite contrary, such
problems confuse students even more because noone really understands the
actual problem behind such examples.

My advice would be to skip the 2nd part of the second course altogether
or to cut it down to a short overview (without assignments).

Just my 2 cents,
Stuart
 
S

Stefan Ram

Stuart said:
IMHO, it makes little sense to teach object-orientation to people who
never had written code before. The best you can expect is probably that
they can write some crappy exercise code, but it takes many years to get
to grips with object orientation (or better: how to divide the problem
space into components).

Yes, I agree. The people who go into the OOP-course, however, have
decided that they want to learn this now for whatever reason. They
do not necessarily have to be the same people as those who were in
the preceding beginner's course. Everyone is free to take the
beginners course, wait 10 years, and then take the »advanced« course.

I have translated a famous English text on this subject into German:

http://purl.net/stefan_ram/html/21-tage
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,187
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top