2

I'm following along some sample code, but I'm getting an error from it that I don't know how to fix properly. I'm getting an error passing *this as a reference. Here's the relevant code:

//ShadeRec.h

class World;

class ShadeRec
{
public:
  World& w;

  ShadeRec(World& wr)
}

ShadeRec::ShadeRec(World& wr) : w(wr) {}

//World.h

#include "ShadeRec.h"
#include "Ray.h"

class World
{
public:
  World();
  ShadeRec hit_bare_bones_objects(const Ray& ray) const;
}

ShadeRec World::hit_bare_bones_objects(const Ray& ray) const
{
    ShadeRec sr(*this);
    //Do stuff with sr
    return sr;
}

The error is happening in hit_bare_bones_objects where I declare ShadeRec sr(*this); The error is:

1> error C2664: 'ShadeRec::ShadeRec(const ShadeRec &)' : cannot convert argument 1 from 'const World' to 'World &'
1> Conversion loses qualifiers

What is the correct way to do this?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
elveatles
  • 2,160
  • 3
  • 18
  • 16
  • 3
    ShadeRec(const World & wr) –  Apr 21 '15 at 18:09
  • This trades one error for another. Now I get: 1> error C2440: 'initializing' : cannot convert from 'const World' to 'World &' 1> Conversion loses qualifiers 1> error C2439: 'ShadeRec::w' : member could not be initialized – elveatles Apr 21 '15 at 18:14
  • 1
    `public: const World& w;` then. Do you intend to changethe World in ShadeRec? –  Apr 21 '15 at 18:23
  • The example code doesn't explain the intention specifically, but I'm guessing they do want the ability to change ShadeRec::w. However, what you suggest seems perfectly valid. – elveatles Apr 21 '15 at 18:41

5 Answers5

3

"What is the correct way to do this?"

You either need to define the constructor with a const reference parameter

  ShadeRec(const World& wr);
        // ^^^^^

or declare your

  ShadeRec hit_bare_bones_objects(const Ray& ray); // <<< no const

member function in the World class non const.

Which is the correct way depends on //Do stuff with sr includes operating on a non const World instance, or not.

Class function members declared as const anyway only have access to a const this pointer, which can only be dereferenced as a const reference in turn.


A 3rd option is to use a const_cast<> when interacting with the interface of the other class. That's not necessarily recommended, and you should really know what you're doing. It's of a similar danger level, like using reinterpret_cast<>1:

ShadeRec World::hit_bare_bones_objects(const Ray& ray) const {
    ShadeRec sr(*const_cast<World*>(this));
              // ^^^^^^^^^^^^^^^^^^^    ^
    //Do stuff with sr
    return sr;
}

1) I'm using const_cast<> occasionally, when I'm annoyed by other API's (I can't change or patch), that don't get const correctness right. I still don't want let to creep in their faults into my code (as long I'm sure about doing so).

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • Thanks. I went with declaring the hit_bare_bones_objects method as non const. I was not aware that it made *this constant until you wrote it. A lot of the answers say to use ShadeRec(const World& wr); but this trades one error for another since ShadeRec::w cannot be initialized when the constructor is changed in this way. – elveatles Apr 21 '15 at 18:28
  • 1
    @elveatles If you use `const` for parameters and class function members, you need to do it consequently and concisely. Introducing `const` correctness for an existing code base, that didn't care much about, can become a quite tedious task quickly. I agree for such case, sometimes simply leaving it alone (beyond better knowledge), is the better and more pragmatic decision. If you're building a code base from scratch, keep `const` correctness wherever it should occur. – πάντα ῥεῖ Apr 21 '15 at 18:33
1

ShadeRec::ShadeRec is saying "the world wr you give me is non-constant; no guarantees it will remain unchanged". ShadeRec sr(*this); is saying "here's a world that is constant, i.e. mustn't be changed". That's a conflict. To solve it, either make ShadeRec(World& wr) (and World& w) constant, or make World::hit_bare_bones_objects non-constant.

Dabbler
  • 9,733
  • 5
  • 41
  • 64
0

Make your parameter declaration of ShadeRec constructor const:

ShadeRec::ShadeRec( const World& wr) 

When you define hit_bare_bones_objects, it is marked as const, which means that the function is not allowed to change the state of this. If you passed *this by non-const reference in the constructor of ShadeRec it could allow ShadeRec to violate that const declaration. Hence, you pass by const reference.
You could also remove the const from hit_bare_bones_objects and then you could leave the ShadeRec constructor as is.

Make sense?

edtheprogrammerguy
  • 5,957
  • 6
  • 28
  • 47
0

In your method:

ShadeRec World::hit_bare_bones_objects(const Ray& ray) const

You have told the compiler that you will not modify any of the objects in World.

You will need to pass the *this as a constant reference.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
0

You are getting this error because you are passing *this inside a const method. So this is also const here, and can be used only in const context.

So, if you want to keep this method const then you should, make:

ShadeRec::ShadeRec(const World& wr) : w(wr) {}
                   ^^^^^

but also World& w; inside ShadeRec should be changed to const World& w;, which might be not what you want.

marcinj
  • 48,511
  • 9
  • 79
  • 100