Overly Strict Const Overloading

This is a discussion on Overly Strict Const Overloading within the C++ Programming forums, part of the General Programming Boards category; I've run into some strange linkage problems with gcc (and MSVC) with regards to finding an overloaded stream operator in ...

  1. #1
    Registered User
    Join Date
    Apr 2007
    Posts
    129

    Overly Strict Const Overloading

    I've run into some strange linkage problems with gcc (and MSVC) with regards to finding an overloaded stream operator in a C++ library I created. The gist of it is very simple, I have a class called gpumat. This class overloads the << operator like this:

    Code:
    class gpumat {
    /* stuff */
    
    } ;
    
    std::ostream& operator<<(std::ostream& output, const gpumat & p) // print a gpumat
    {
    /* implementation */
    }
    
    /* .....  usage */
    using namespace std ;
    gpumat g(3,4) ;
    g.cgauss()
    cout << g << endl ;
    Note the const identifier before the &p input argument.
    For gcc the linker will fail with something like:
    undefined reference to `operator<<(std::basic_ostream<char, std::char_traits<char> >&, gpumat&)'.

    This is baffling to me since it's perfectly legal to pass a non const. variable into a function that has a const specifier. The const is supposed to mean that the function will not change it's input. Moreover if I wanted to print a temporary computation, the const declaration is required as I understand it, ie:
    Code:
    gpumat a, b ;
    /* initialization */
    cout << a + b << endl ;
    Microsoft's cl.exe will fail to link if you do not have the const specifier in front of &p if you pass a temporary class into the << operator (or any other overloaded function actually).

    If I remove the const token gcc compiles links and runs my code just fine.
    Code:
    class gpumat {
    /* stuff */
    
    } ;
    
    std::ostream& operator<<(std::ostream& output, gpumat & p) // print a gpumat
    {
    /* implementation */
    }
    
    /* .....  usage */
    using namespace std ;
    gpumat g(3,4) ;
    g.cgauss()
    cout << g << endl ;
    How can this be correct? If it is one would have to supply const and non const versions of every overloaded function/operator for each argument and it would remove any optimization advantage since the non-constant version would be linked in for most usage patterns.

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    undefined reference to `operator<<(std::basic_ostream<char, std::char_traits<char> >&, gpumat&)'.
    This should pretty much mean that you have declared it like that somewhere. Make sure declarations match definitions. Should be const everywhere.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    Registered User
    Join Date
    Apr 2007
    Posts
    129
    Quote Originally Posted by anon View Post
    This should pretty much mean that you have declared it like that somewhere. Make sure declarations match definitions. Should be const everywhere.
    Interesting point actually. Unless I declared it twice (scrambling to double check), I did have the same declaration in both definition and implementation (I did). I suppose it would be too much to ask to get a compiler error instead of a link error if it were wrong.

    This is gcc 4.4.1 that's doing this. In fact it should be a bug if I use the exact same declaration as the implementation, and it compiles correctly but does not link correctly.

    That is if the use of the operator had no declaration that satisfies such use I would get a compiler error I would think, but if it does match then the object code linkage should match as well.

    However I still want to know whether or not ANSI C++ should give me an error in my test case. Shouldn't I actually be able to link against code declared with const reference arguments?

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    The parameter should be const and then the code is correct.

    But the linker error message says that somehow it got the idea there must be an overload with the non-const reference. It doesn't generally pull these ideas from the thin air. It must have been declared at the point where it is used.

    May-be you'd need to rebuild the project (bad object files)?
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  5. #5
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    So using gcc 4.4.1, g++ loves the following example:
    Code:
    #include <iostream>
    
    class gpumat {
        private:
            int x, y;
    
        public:
            int getx() const;
            int gety() const;
            gpumat(int, int);
    };
    
    gpumat::gpumat(int x1, int y1) : x(x1), y(y1) { };
    
    int gpumat::getx() const {
        return x;
    }
    
    int gpumat::gety() const {
        return y;
    }
    
    std::ostream& operator<< (std::ostream& output, const gpumat &p) {
        output << "(" << p.getx() << ", " << p.gety() << ")";
        return output;
    }
    
    using namespace std;
    int main() {
        gpumat g(3,4);
        cout << g << endl;
    }
    So apparently there's something in the /* */ bits that is causing the issue. (I checked: leaving the const off the getx/gety functions gives a different error message:
    Code:
    seven.cpp:24: error: passing ‘const gpumat’ as ‘this’ argument of ‘int gpumat::getx()’ discards qualifiers
    so that seems okay.)

  6. #6
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    interesting. You should post more code, the issue is probably somewhere else

  7. #7
    Registered User
    Join Date
    Jun 2005
    Posts
    6,657
    Quote Originally Posted by SevenThunders View Post
    Interesting point actually. Unless I declared it twice (scrambling to double check), I did have the same declaration in both definition and implementation (I did). I suppose it would be too much to ask to get a compiler error instead of a link error if it were wrong.
    If, at the point where the operator is used, the compiler sees this;
    Code:
    std::ostream& operator<< (std::ostream& output, gpumat &p);  // look ma!!! no const
    then your calling function will attempt to call the non-const version because g is non-const. If you have not implemented a non-const version, the linker detects that and complains.

    Quote Originally Posted by SevenThunders View Post
    This is gcc 4.4.1 that's doing this. In fact it should be a bug if I use the exact same declaration as the implementation, and it compiles correctly but does not link correctly.
    Maybe. Despite the fact you are arguing the problem is with the compiler, there is about a 99.99% chance the problem is in your code rather than with the compiler and linker.

    The two possibilities for what you are seeing are;

    1) Your code, at the point where you use the operator, has provided a declaration of a non-const version of your operator. But you have only implemented the const version.

    2) You have left the object file that contains the implementation of the operator out of the link.

    The first is because you have lied to the compiler, the compiler has accepted your word, and the linker detects that you have lied.

    The second is because the linker is required to only search through object files or libraries it is told to. If you do not supply a needed object file (i.e. one that contains a needed function) the linker rightfully complains about not finding that function.

    Quote Originally Posted by SevenThunders View Post
    That is if the use of the operator had no declaration that satisfies such use I would get a compiler error I would think, but if it does match then the object code linkage should match as well.
    That is correct. The fact you are not getting such an error means you have such a function declared. The fact you are getting a linker error means that the linker is not able to find the function you declared to the compiler. As per the reasoning I gave above.

    The problem is still in your code, not with the compiler. A compiler is an ignoramous that does what it does, regardless of what you expect it to do.

    Quote Originally Posted by SevenThunders View Post
    However I still want to know whether or not ANSI C++ should give me an error in my test case. Shouldn't I actually be able to link against code declared with const reference arguments?
    If you've described your test case accurately, the ISO standard does not require a compiler error.

    The compiler is certainly not required to complain if your code has declared a non-const version of the operator. When working with a non-const object (g in your code), the compiler is potentially able to call a non-const or a const version of the operator. If you have declared both, the ISO C++ standard requires that the non-const version be called and not the const version (the non-const version is a better match to the object). If you have declared only the non-const version that is the one which is required to be called.

    Either way, if the non-const version is required to be called, then the compiler is not required to complain. The linker will, however, complain if it cannot find that function.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Sunshine, and read this, this, and this before posting again.

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    To find the erroneous declaration that has a non-const argument, comment out the const version of the operator overload, then try to stream a const object.
    Code:
    const gpumat cgm;
    std::cout << cgm;
    The compiler should emit an error about not being able to find a suitable overload, and hopefully also a note pointing the unsuitable candidate overload that takes the non-const argument.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. why can't my perceptron learn correctly?
    By yann in forum C Programming
    Replies: 25
    Last Post: 10-15-2010, 01:26 AM
  2. Need help implementing a class
    By jk1998 in forum C++ Programming
    Replies: 8
    Last Post: 04-05-2007, 04:13 PM
  3. Problem with Template Function and overloaded equality operator
    By silk.odyssey in forum C++ Programming
    Replies: 7
    Last Post: 06-08-2004, 05:30 AM
  4. Another overloading "<<" problem
    By alphaoide in forum C++ Programming
    Replies: 18
    Last Post: 09-30-2003, 11:32 AM
  5. Color Variety
    By Unregistered in forum C++ Programming
    Replies: 7
    Last Post: 10-23-2002, 10:17 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21