2

I have a class with a constructor which takes a non-const reference which is assigned to a member of the class. Now I want to create a const object of said class, but the constructor complains if I pass a const reference to the constructor. The code below which is a simplification of my original code demonstrates the problem.

As far as i can tell, there should be no problems with creating a const object since it is based on const data?

How can achieve what I'm trying to do in create_const_a?

#include <iostream>

class A {
private:
  double &d;
  const int &i;
public:
  A(double &dd, const int &ii)
    : d(dd), i(ii)
  {}

  void set_d(double a) {
    d = a;
  }

  void print() const {
    std::cout << d << " " << i << std::endl;
  }
};


A create_a(double &dd, const int &ii) {
  return A(dd,ii);
}

const A create_const_a(const double &dd, const int &ii) {
  return A(dd,ii);
}


void foo(A a)
{
  a.set_d(1.3);
  a.print();
}

void bar(const A a)
{
  a.print();
}


int main(int argc, char *argv[])
{
  double d = 5.1;
  int i = 13;

  foo(create_a(d,i));

  bar(create_const_a(d,i));

  return 0;
}

The error I get is:

test.cc: In function ‘const A create_const_a(const double&, const int&)’:
test.cc:27:17: error: binding ‘const double’ to reference of type ‘double&’ discards qualifiers
   return A(dd,ii);
                 ^
test.cc:8:3: note:   initializing argument 1 of ‘A::A(double&, const int&)’
   A(double &dd, const int &ii)
   ^

Update: After learning some new things about how const works with objects and non-const references within them, I eventually solved the original problem by introducing another type, say ConstA which only contains const references, which could then be used in each of the problematic cases.

9
  • 1
    You can go from non-const to const, but not vice-versa. The constructor needs a non-const, and your create_const_a receives a const. Commented Jan 18, 2017 at 11:59
  • 5
    All those references... If you continue you will sooner or later come to a pointer where you have a dangling reference and bad things will happen. Why are you having references in the A class? What is the actual problem you are trying to solve with solution like this? Commented Jan 18, 2017 at 12:00
  • 1
    Your create_const_a method is returning a reference to a local object. This does not work the way you seem to think it does. See this answer for more information. Commented Jan 18, 2017 at 12:01
  • 2
    @FrançoisAndrieux No it returns a reference to a temporary object. Fortunately it's a const reference which prolongs the lifetime of the object until the end of the expression it is used in. Still not a good idea, but in this very specific case it will work. Commented Jan 18, 2017 at 12:06
  • Okay, returning a reference to a local variable is clearly invalid, but somehow returning a const reference seems to work fine. But that's a separate discussion and doesn't affect the present issue. I updated the question now to remove that. Commented Jan 18, 2017 at 12:07

3 Answers 3

3

C++ prohibits this to avoid conversion of const reference to non-const.

Here is a small example of how this would happen:

struct foo {
    int& a;
    foo(int& b) : a(b) {}
    void bar() const {
        a = 5;
    }
};

The above compiles well, because a = 5 does not change the state of the foo object; it changes the state of an external int, so foo::bar is allowed to be const.

Now assume that we could do this:

const foo make_const(const int& x) {
    return foo(x); // Not allowed
}

Then we would be able to write

const foo f(make_const(10));
f.bar();

and modify a reference to a temporary int, which is undefined behavior.

Here is a small demo:

int x = 10;
cout << x << endl;
const foo f(x);
f.bar();
cout << x << endl;

It prints

10
5
Sign up to request clarification or add additional context in comments.

2 Comments

The fact that the reference can be changed without violating const-correctness was completely new to me and explains a lot about why this seems impossible.
@kalj That's the trickiest part of const-ness when it applies to references and pointers. A pointer or a reference can be const while the item to which they point / reference would remain writable.
0

The function const A create_const_a takes as first argument a ref to a const double, and it then try to use it in ctor as a non const ref which is not allowed.

You should remove the const qualifier here.

If you are really sure of why you can do it here, that means if you can be sure that create_const will allways receive non const parameters, you can explicitely use const_cast to remove the qualifier:

const A create_const_a(const double &dd, const int &ii) {
  return A(const_cast<double &>(dd),ii);
}

But BEWARE, this will lead to Undefined Behaviour if you do that on a variable that was initially declared as const.

Comments

0

You are trying to drop constness:

const A create_const_a(const double &dd, const int &ii) {
  return A(dd,ii);
}

but dd is a non-const, so the created object A would not know that it cannot modify it's d member, while the caller of create_const_a would pass the variable believing so.

You cannot go around with out making a copy of dd, or dropping the const (e.g. through a const_cast) which is dangerous and strongly indicates an error in the design.

Your definition of A says "I need to be able to modify d", but in the create_const you promise that it won't be modified.

In other words, you should address the error in the design. Either release the constraint or make a copy. Making the "error go away" will just silence the symptom of bad design until you will get into real trouble when the breach of the contract ends up in repercussions.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.