Which constructor?

Discussion in 'C++' started by Stefan Ram, Mar 13, 2014.

  1. Stefan Ram

    Stefan Ram Guest

    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" }; }
     
    Stefan Ram, Mar 13, 2014
    #1
    1. Advertisements

  2. This is direct initialization (construction), parameterized on char
    const*, I suppose (if there is one with that argument).
    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
     
    Victor Bazarov, Mar 13, 2014
    #2
    1. Advertisements

  3. 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.
     
    Barry Schwarz, Mar 13, 2014
    #3
  4. Stefan Ram

    Stefan Ram Guest

    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.
     
    Stefan Ram, Mar 13, 2014
    #4
  5. 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.
    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
     
    Victor Bazarov, Mar 13, 2014
    #5
  6. Stefan Ram

    Stefan Ram Guest

    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.
     
    Stefan Ram, Mar 13, 2014
    #6
  7. Stefan Ram

    Stefan Ram Guest

    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?)
     
    Stefan Ram, Mar 14, 2014
    #7
  8. That's how 8.5/16 describes it.
    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
     
    Victor Bazarov, Mar 14, 2014
    #8
  9. Stefan Ram

    Stefan Ram Guest

    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!
     
    Stefan Ram, Mar 14, 2014
    #9
  10. 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.
    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++.
    Don't play games with them about it. Not in the beginning.
    Be simpler. Don't frighted them with all the complexity, ease them into
    it. That's how I'd do it.

    V
     
    Victor Bazarov, Mar 14, 2014
    #10
  11. Stefan Ram

    Stefan Ram Guest

    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«.
    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".
    I would love to choose one single initialization syntax and
    then only use this syntax. But this is not possible in C++.
     
    Stefan Ram, Mar 14, 2014
    #11
  12. Stefan Ram

    Stefan Ram Guest

    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.
     
    Stefan Ram, Mar 14, 2014
    #12
  13. Stefan Ram

    Stuart Guest

    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
     
    Stuart, Mar 14, 2014
    #13
  14. Stefan Ram

    Stefan Ram Guest

    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
     
    Stefan Ram, Mar 14, 2014
    #14
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.