Templates, copy constructor and operator<<

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • franky.backeljauw@ua.ac.be

    Templates, copy constructor and operator<<

    Hello,

    I have a problem with using a copy constructor to convert an object of a
    templated class to object of another templated class. Let me first
    include the code (my question is below):

    <code:templates .h>
    #include <string>
    #include <iostream>

    using namespace std;

    template<class T> class tempo;

    template<class T>
    class delo
    {
    private:
    tempo<T> a;
    tempo<T> b;
    public:
    delo () : a(0), b(0) {}
    delo ( T c, T d ) : a(c), b(d) {}

    void evaluate( tempo<T>& c );
    };

    template<class T> void printValue( tempo<T> );
    template<class T> ostream& operator<< ( ostream&, const tempo<T>& );

    template <class T>
    class tempo
    {
    private:
    T value;

    public:
    tempo () : value(0) {}
    tempo ( T init ) : value(init) {}
    tempo ( delo<T> d ) { d.evaluate(*thi s); }

    friend ostream& operator<< <T>( ostream&, const tempo& );
    void compute( tempo& a, tempo& b );
    };

    template class delo<int>;
    template class tempo<int>;
    </code:templates. h>

    <code:templates .cpp>
    #include "templates. h"
    #include <iostream>

    template <class T>
    void delo<T>::evalua te( tempo<T>& c )
    {
    c.compute( a, b );
    }

    template <class T>
    void tempo<T>::compu te( tempo<T>& a, tempo<T>& b )
    {
    value = a.value + b.value;
    }

    template <class T>
    ostream& operator<< ( ostream& o, const tempo<T>& t )
    {
    o << "current value = " << t.value << endl;
    return o;
    }

    template ostream& operator<< ( ostream&, const tempo<int>& );
    </code:templates. cpp>

    The class "delo" is used to delay the computation (compute, in this
    example an addition) until it needs to be executed, e.g. when a left-side
    object is given or when the result is requested for instance using cout.
    In this case, the "delo" object should be converted to a "tempo" object
    using the copy constructor, which calls the evaluate function, in which
    the compute function is actually called.

    So far, so good ... now look at a test program:

    <code:test.cp p>
    #include "templates. h"
    #include <iostream>

    using namespace std;

    int main ()
    {
    tempo<int> anInt( 123 );
    cout << anInt << endl;

    delo<int> aDelInt( 789, 211 );
    cout << aDelInt << endl;
    }
    </code:test.cpp>

    The first cout line compiles without any problem, but the second cout line
    results in a long error message, which goes like:

    <error>
    g++ -c test.cpp -o test.o
    test.cpp: In function `int main()':
    test.cpp:19: no match for `std::ostream& << delo<int>&' operator
    /usr/include/c++/3.2.2/bits/ostream.tcc:55: candidates are:
    std::basic_ostr eam<_CharT, _Traits>& std::basic_ostr eam<_CharT,
    _Traits>::opera tor<<(std::basi c_ostream<_Char T,
    _Traits>&(*)(st d::basic_ostrea m<_CharT, _Traits>&)) [with
    _CharT = char,
    _Traits = std::char_trait s<char>]
    /usr/include/c++/3.2.2/bits/ostream.tcc:77:
    std::basic_ostr eam<_CharT, _Traits>& std::basic_ostr eam<_CharT,
    _Traits>::opera tor<<(std::basi c_ios<_CharT,
    _Traits>&(*)(st d::basic_ios<_C harT, _Traits>&)) [with _CharT =
    char, _Traits
    = std::char_trait s<char>]
    /usr/include/c++/3.2.2/bits/ostream.tcc:99:
    std::basic_ostr eam<_CharT, _Traits>& std::basic_ostr eam<_CharT,
    _Traits>::opera tor<<(std::ios_ base&(*)(std::i os_base&)) [with
    _CharT = char,
    _Traits = std::char_trait s<char>]
    /usr/include/c++/3.2.2/bits/ostream.tcc:171 :
    std::basic_ostr eam<_CharT, _Traits>& std::basic_ostr eam<_CharT,
    _Traits>::opera tor<<(long int) [with _CharT = char, _Traits =
    std::char_trait s<char>]
    [...]
    /usr/include/c++/3.2.2/ostream:211:
    std::basic_ostr eam<_CharT,
    _Traits>& std::operator<< (std::basic_ost ream<_CharT, _Traits>&,
    char) [with
    _CharT = char, _Traits = std::char_trait s<char>]
    make: *** [test.o] Error 1
    </error>

    This says it cannot output the "delo" object, while it could copy this
    object to a "tempo" object using the copy constructor. But it seems like
    it does not know about this copy constructor, or that it cannot use it.
    Copying it explicitly (that is, using "cout << tempo<int>( aDelInt )" in
    test.cpp does work.

    If I use non-templated versions of these classes, it works fine and it
    calls the copy constructor like it should.

    So my questions are: why does this give an error, and why is it not using
    the copy constructor? Are there any limitations? And if so, is there
    another way to solve this (without having to do it myself explicitly)?

    -- Thanks for any reply.

    Franky Backeljauw
    University of Antwerp
    Department of Mathematics and Computer Science
  • Brian MacBride

    #2
    Re: Templates, copy constructor and operator&lt;&lt ;


    <franky.backelj auw@ua.ac.be> wrote in message
    news:Pine.GSO.4 .50.03070401281 60.6859-100000@hmacs.ru ca.ua.ac.be...[color=blue]
    > Hello,
    >
    > I have a problem with using a copy constructor to convert an object of a
    > templated class to object of another templated class. Let me first
    > include the code (my question is below):
    >[/color]

    // Snip a bunch_a_code...
    [color=blue]
    >
    > So far, so good ... now look at a test program:
    >
    > <code:test.cp p>
    > #include "templates. h"
    > #include <iostream>
    >
    > using namespace std;
    >
    > int main ()
    > {
    > tempo<int> anInt( 123 );
    > cout << anInt << endl;
    >
    > delo<int> aDelInt( 789, 211 );
    > // cout << aDelInt << endl;[/color]

    cout << (tempo <int>) aTemInt << endl;
    [color=blue]
    > }
    >[/color]
    // Snip. a bunch_a_error_m essage
    [color=blue]
    > Copying it explicitly (that is, using "cout << tempo<int>( aDelInt )" in
    > test.cpp does work.[/color]

    cout << (tempo <int>) aTemInt << endl;

    Yields...

    current value = 123

    current value = 1000

    That what you wanted??

    Personally, I wouldn't recommend this or any other cast here...
    [color=blue]
    > If I use non-templated versions of these classes, it works fine
    > and it calls the copy constructor like it should.[/color]
    [color=blue]
    > So my questions are: why does this give an error, and why is it
    > not using the copy constructor? Are there any limitations?
    > And if so, is there another way to solve this (without having to
    > do it myself explicitly)?[/color]

    That is what I'd do...

    const tempo <int> aTemInt (aDelInt);
    cout << aTemInt << endl;
    [color=blue]
    >
    > -- Thanks for any reply.
    >[/color]

    HTH
    [color=blue]
    > Franky Backeljauw
    > University of Antwerp
    > Department of Mathematics and Computer Science[/color]

    Regards

    Brian

    Comment

    • franky.backeljauw@ua.ac.be

      #3
      Re: Templates, copy constructor and operator&lt;&lt ;

      On Thu, 3 Jul 2003, Brian MacBride wrote:
      [color=blue]
      > cout << (tempo <int>) aTemInt << endl;
      >
      > Yields...
      >
      > current value = 123
      >
      > current value = 1000
      >
      > That what you wanted??
      >
      > Personally, I wouldn't recommend this or any other cast here...
      >
      > That is what I'd do...
      >
      > const tempo <int> aTemInt (aDelInt);
      > cout << aTemInt << endl;[/color]

      Actually, I was wondering why "cout << aDelInt << endl;" does not work. I
      have a copy constructor that can copy from "delo" to "tempo", but it does
      not get called automatically. This means I cannot output a "delo" object,
      although it works when these classes are not templated.

      So my guess is that it has something to do with templates again ... doh.

      -- Regards,

      Franky Backeljauw
      University of Antwerp
      Department of Mathematics and Computer Science

      Comment

      • Michiel Salters

        #4
        Re: Templates, copy constructor and operator&lt;&lt ;

        franky.backelja uw@ua.ac.be wrote in message news:<Pine.GSO. 4.50.0307040128 160.6859-100000@hmacs.ru ca.ua.ac.be>...[color=blue]
        > Hello,
        >
        > I have a problem with using a copy constructor to convert an object of a
        > templated class to object of another templated class. Let me first
        > include the code (my question is below):[/color]
        [color=blue]
        > template<class T> class tempo;
        >
        > template<class T>
        > class delo
        > { /* snip */
        > public:
        > delo () : a(0), b(0) {}
        > delo ( T c, T d ) : a(c), b(d) {}
        >
        > void evaluate( tempo<T>& c );
        > };[/color]
        [color=blue]
        > template <class T>
        > class tempo
        > { /* snip */
        > public:
        > tempo () : value(0) {}
        > tempo ( T init ) : value(init) {}
        > tempo ( delo<T> d ) { d.evaluate(*thi s); }
        > };
        > /*snip/[/color]
        [color=blue]
        > The class "delo" is used to delay the computation (compute, in this
        > example an addition) until it needs to be executed, e.g. when a left-side
        > object is given or when the result is requested for instance using cout.[/color]

        This all looks simple and reasonable. However, the compiler can't assume
        things are this simple. There might be template specializations of tempo
        (e.g. when T==float: tempo<float>) where there is an additional ctor
        (e.g. tempo( delo<double> d ) ). Now if the compiler has a delo<double>
        it would be ambiguous which tempo<T> to create. Worse, unless the compiler
        instantiates delo<T> for all Ts, it doesn't actually know which delo<T>
        instances have a delo<double>. And "all Ts" is an infinitely large set.

        Therefore the compiler isn't required or even allowed to take implicit
        template instantiations into account when doing implicit casts. As
        pointed out, if you specify the type T, it will succeed.

        Regards,
        --
        Michiel Salters

        Comment

        • Brian MacBride

          #5
          Re: Templates, copy constructor and operator&lt;&lt ;


          <franky.backelj auw@ua.ac.be> wrote in message
          news:Pine.GSO.4 .50.03070409583 60.2668-100000@hmacs.ru ca.ua.ac.be...[color=blue]
          > On Thu, 3 Jul 2003, Brian MacBride wrote:
          >[color=green]
          > > cout << (tempo <int>) aTemInt << endl;
          > >
          > > Yields...
          > >
          > > current value = 123
          > >
          > > current value = 1000
          > >
          > > That what you wanted??
          > >
          > > Personally, I wouldn't recommend this or any other cast here...
          > >
          > > That is what I'd do...
          > >
          > > const tempo <int> aTemInt (aDelInt);
          > > cout << aTemInt << endl;[/color]
          >
          > Actually, I was wondering why "cout << aDelInt << endl;" does not work. I
          > have a copy constructor that can copy from "delo" to "tempo", but it does
          > not get called automatically. This means I cannot output a "delo" object,
          > although it works when these classes are not templated.
          >[/color]

          Well, I think it would be a bit of a stretch for the compiler to know
          exactly what you intended without some guidance. It could reasonably assume
          that you wanted to convert delo <onething> to tempo <whatever> ;-).
          [color=blue]
          > So my guess is that it has something to do with templates again ... doh.
          >
          > -- Regards,
          >
          > Franky Backeljauw
          > University of Antwerp
          > Department of Mathematics and Computer Science[/color]

          Regards,

          Brian

          Comment

          Working...