Blag

He's not dead, he's resting

C++ Const Curiosity

GCC will accept the following (look closely at S::f‘s signature in both places):

struct T
{
    void foo()
    {
    }
};

struct S
{
    void f(const T);
};

void
S::f(T t)
{
    t.foo();
}

int main(int, char *[])
{
    T t;
    S s;
    s.f(t);
}

The question is, should it?

Update: and the answer is, yes, it should, according to [dcl.fct] in the standard. This is both useful and annoying.

Advertisements

8 responses to “C++ Const Curiosity

  1. funnyeyes July 28, 2009 at 10:06 pm

    This is one of the whims of the language, and const overload resolution is known to have a SFINAE like behavior. Try this as well:

    #include <iostream>
    
    struct T
    {
        int j;
        T() : j(0) {};
        void foo()
        {
            j++;
        }
    };
    
    struct S
    {
        void f(const T) const;
        void f(T);
    };
    
    void S::f(const T t) const { // look at the consty here again...
        std::cout << t.j << std::endl;
    }
    
    void S::f(T t) { // try putting const T t
        t.foo();
        std::cout << t.j << std::endl;
    }
    
    int main(int, char *[])
    {
        const T t;
        S s;
        s.f(t); // kiss constness bye bye?
    }
    
    • Ciaran McCreesh July 28, 2009 at 10:26 pm

      You’re not kissing constness bye bye, though, since t is passed by value. It’s a copy that gets modified, not the original object. If you make T’s copy constructor private it won’t compile.

  2. Jacopo July 28, 2009 at 10:32 pm

    Why not? By using “const” you are basically promising you won’t touch the passed object. In this case, since you are getting a local copy of the object, it’s just an aid for your memory and for future reference.

    Maybe you were wondering: “why doesn’t the compiler suggest I use a const T&, which is syntactically (and hopefully semantically) equivalent?” The answer could be that some data is smaller than a pointer, so passing it by value can make sense.

    Does this answer your question? Or I missed something in your code?

    • Ciaran McCreesh July 28, 2009 at 10:40 pm

      I’m asking whether it’s an intentional language design decision that the type signatures don’t have to match, or whether it’s GCC being wonky.

      Note also that the const does have to match on by-value return values.

  3. Bruno July 29, 2009 at 12:34 am

    Well.. i don’t know if GCC should accept it or not, but Visual C++ 2008 also accept it without warning.

    So if it’s a wrong implementation of language design, we have more than one (big) compiler with this problem.

  4. Tomalak Geret'kal July 31, 2009 at 4:24 pm

    Why is this wrong?

    struct A {
      void f(const int);
    };
    
    void A::f(int x) {
      std::cout << x;
    }
    
    int main() {
      A().f(3);   // output: 3
    }
    

    It’s because consting a passed-by-value object doesn’t really mean anything. The object will have a local copy made and anything done to it does not exist past the lifetime of the function.

    Compare with:

    struct A {
      void f(const int&);
    };
    
    void A::f(int& x) {
      std::cout << x;
    }
    
    int main() {
      A().f(3);   // output: error: prototype for `void A::f(int&)` does not match any in class `A`
    }
    

    ^ Where the const-specifier actually means something, it doesn’t compile.
    So const on arguments passed by value is ignored as not relevant.

    I’ll see if I can find this in the standard, but I don’t see it as a bug or a flaw in any way… merely an [perhaps slightly unintuitive at first glance] curiosity of life. :)

  5. tomalak July 31, 2009 at 4:30 pm

    Actually, scrub that.

    Compare:

    void f(int x);
    void f(const int x) { x = 5; }
    int main() {
      f(3); //error: assignment of read-only parameter `x`
    }

    with:

    struct A {
      void f(const int);
    };
    void A::f(int x) {
      x = 5;
      std::cout << x;
    }
    int main() {
      A().f(3); // output: 5
    } 

    I guess `const` simply does not have meaning on a by-value parameter in a *declaration* because calling scopes are not affected by it. So it’s ignored. Whereas on the definition it has the usual behaviour.

    Definitely seems counter-intuitive to me, but at least it makes sense practically.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s