0

Suppose I have

class A final { 
    int& ir; 
public:
    A(int& x) : ir(x) { }
    void set(int y) { ir = y; } // non-const method!
    int get() const { return ir; }
};

and

const int i;

Obviously I can't have

 A a(i);

since that would breaks constness. But I also cannot have

 const A a(i);

despite the fact that this will not break constness de-facto. C++ doesn't support "const-only" ctors, e.g. in this case one which would take a const int&. Is there a way to get const A a wrapping a reference to i - other than

A a(const_cast<int &>(i))

?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 1
    Could you please describe what you are trying to achieve? – paceholder Apr 21 '16 at 15:03
  • "despite not breaking constness"? I don't understand. You can afterwards write to `a.ir`. I would say that's pretty unsafe. – Johannes Schaub - litb Apr 21 '16 at 15:03
  • What comes to mind is `class A { variant v; A(const int&a):a(a) { } A(int &a):a(a) { } };`. not sure whether this works as-is, but could be a start. Then visit `a.v` with a `[](auto &&a){}` – Johannes Schaub - litb Apr 21 '16 at 15:05
  • @JohannesSchaub-litb: Edited in the hopes of being clearer. Note ir is private. Your suggestion is interesting and I'm thinking about it. – einpoklum Apr 21 '16 at 15:08
  • @paceholder: See edit. A is a sort of a facade for an int (in reality I have a more complex facade for something else). I should be able to construct a facade which can mutate its backing int, if that int is non-const, but also construct the "same" facade, const, with a const int backing it. It seems like C++ is almost forcing me to choose either or. – einpoklum Apr 21 '16 at 15:10
  • Instead of `const A`, you can settle for `A` or `A<>`. And modify `ir` adequately depending on the template parameter. – Johannes Schaub - litb Apr 21 '16 at 15:12
  • I can't imagine set() function in the const case. – paceholder Apr 21 '16 at 15:24
  • Note that this is pretty much the same problem as with `iterator`s and `const_iterator`s. There are workarounds, but no language feature to solve that problem. – Quentin Apr 21 '16 at 15:28
  • @Quentin: I think I see what you mean, but if you could link someplace discussing this it would be nice. – einpoklum Apr 21 '16 at 15:30
  • Isn't this what factories are for? – Lightness Races in Orbit Apr 21 '16 at 15:32
  • @LightnessRacesinOrbit: No, unless I misunderstand what you mean. – einpoklum Apr 21 '16 at 15:54
  • @einpoklum: What else are they for then? – Lightness Races in Orbit Apr 21 '16 at 16:21
  • @LightnessRacesinOrbit: Factories are for [creating objects](https://en.wikipedia.org/wiki/Factory_%28object-oriented_programming%29), in general. But - factories have to be coded too, they don't work magically. The solution to this problem could theoretically be placed inside a factory but the question remains of how to construct a `const A` with a `const int&`, factory or no. – einpoklum Apr 21 '16 at 16:49
  • @einpoklum: The factory would construct an `A`, use whatever mutators are needed to fully configure it, then give you it as a `const` (be it via copying/moving, or reference, or whatever). I thought that was fairly typical usage of factories. – Lightness Races in Orbit Apr 21 '16 at 16:55
  • @LightnessRacesinOrbit: Great, just write an answer explaining how the factory would construct an A when it is given a `const int &`.... – einpoklum Apr 21 '16 at 17:02

5 Answers5

4

"Const" means that the object is constant during between the constructor end and destructor begin (during construction, you must be able to change the object). C++ doesn't have a "preparation constructor" of sorts that is preferred for const objects.

You can try this workaround by applying boost::variant. This is not completely type-safe at compile time, but detect errors at runtime by throwing an exception at least.

#include <boost/variant.hpp>
#include <iostream>

class R {
  boost::variant<int&, const int&> v;
public:
  R(int&v):v(v) { }
  R(const int&v):v(v) { }

  // works with both
  int get() const { 
     return boost::apply_visitor( 
        [](int x){return x;}, v); 
  }

  // only works for non-const A. If at construction, a const
  // int was passed, throws an error at runtime
  void set(int x) {
     boost::get<int&>(v) = x;
  }
};


int main() {
  int a = 0;
  const int b = a;
  R r1(a);
  R r2(b);
  const R r3(a);
  std::cout << r1.get() << r2.get() << r3.get();
  // r3.set(1); // compile error
  r1.set(1); // works
  r2.set(1); // runtime error
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1. Isn't variant in the standard already? 2. How slow is apply_visitor? – einpoklum Apr 21 '16 at 15:28
  • 1
    @einpoklum I believe variant is in C++17 (I could be wrong). Definitely not C++11 or C++14. apply_visitor is a switch statement basically (perhaps a bit more fancy in the boost impl, I have no idea). But since both switch cases here end up executing the same code (simply a move of an int), I would expect the compiler to fold them and inline the entire apply_visitor. If in doubt, ignore until it causes performance problems :) – Johannes Schaub - litb Apr 21 '16 at 15:31
3

C++ doesn't have a concept of fully constant classes, and compiler may not perform if reference int & is used only as const int &. So basically, you cant do it.

LibertyPaul
  • 1,139
  • 8
  • 26
1

It's weird but

class A final {
    union{
    int& ir;
    const int &cir;
    };
public:
    A(int& x) : ir(x) { }
    A(const int& x) : cir(x) { }
    void set(int y) { ir = y; } // non-const method!
    int get() const { return ir; }
};

int main(int argc, char *argv[])
{
    const int cv = 8;
    int v = 6;
    A a( cv );
    std::cout << a.get() << std::endl;
    a.set( v );
    std::cout << a.get() << std::endl;
    return 0; 
}

Also your set and get method opearates on values not references, so it's looks like you a doing something wrong

Jeka
  • 1,364
  • 17
  • 33
  • I would almost upvote this for it's simplicity, if only the standard allowed reference data members inside unions. – eerorika Apr 21 '16 at 15:57
  • @user2079303 this also wouldn't really work: His example would compile and produce undefined behavior, since nothing stops him from calling `a.set(v)`. You need to remember what constructor you took, by for example using a `boost::variant` or storing it manually. – Johannes Schaub - litb Apr 21 '16 at 16:01
  • @JohannesSchaub-litb good point. It seems that the simplicity is not worth it in this case. – eerorika Apr 21 '16 at 16:07
  • @JohannesSchaub-litb could you explain what kind of undefinite behavior you a talking about? – Jeka Apr 21 '16 at 16:50
  • @jeka modifying a "const int" is causes undefined behavior. – Johannes Schaub - litb Apr 22 '16 at 07:17
1

This is not how I would write the code but it somehow works. If you construct a template class from the const variable, the set() function is defined also const and does not change the state. By constructing from non-const variable, the set function is able to change the state.

#include <iostream>
#include <string>
#include <type_traits>

template<typename T>
class A
{
    T &_i;
public:
    A(T &i) : _i(i) {}

   template <typename D = T,
             typename = std::enable_if_t<std::is_const<D>::value
                                        >
            >
    void set(T i) const { std::cout << "Do nothing on set" << std::endl; }

   template <typename D = T,
             typename = std::enable_if_t<!std::is_const<D>::value>
            >    
    void set(T i) { std::cout << "Do something on set" << std::endl; }
};

int main()
{
    {
        std::cout << "Construct from NON const variable" << std::endl;
        int b = 5;

        A<decltype(b)> a(b);   

        a.set(3);
    }

   {
        std::cout << "Construct from const variable" << std::endl;
        int const  b = 5;

        A<decltype(b)> a(b);      

        a.set(3);
    }
}

This prints:

Construct from NON const variable
Do something on set
Construct from const variable
Do nothing on set
paceholder
  • 1,064
  • 1
  • 10
  • 21
0
class A { 
    const int& ir; //  <<-- it's a const int. 
    /* ... */
public:
    A(int& x) : ir(x) { }
};
Itay Grudev
  • 7,055
  • 4
  • 54
  • 86
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Not what I meant, I want to have some non-const A's and some const A's, and to be able to construct the const A's using `const int&`'s – einpoklum Apr 21 '16 at 15:08