1

The question: How can I make it so that a const reference is returned publicly and a non const reference returned privately?

I'm trying to create a read-only template for some variables in my classes. This involves a template class which returns a const reference to the data when public. However in the class I need to operate on the data so I am trying to return a reference that is not const privately. Here's the basics:

private: operator T&() { return data; }
public: operator const T&() const { return data; }

When I add the non const reference as shown above, if I try to access the variable publicly my Visual Studio 2010 cl.exe compiler tells me it cannot access the private member in the class. Something as simple as cout << myobj.x << endl if x was declared using the template will fail.

error C2248: 'proxy<T,C>::operator int &' : cannot access private member declared in class 'proxy<T,C>'

Here is the other thread for reference:
C++ - How to make read only class member variables in Visual Studio 2010 - Stack Overflow

Edit: You asked for the code so here it is.

template <class T, class C>
class proxy {
    friend C;
private:
    T data;
    T operator=(const T& arg) { data = arg; return data; }
    operator T&() { return data; } // I'd expect this is only returned privately
public:
    operator const T&() const { return data; }
};

class myClass {
public:
    proxy<int,myClass> x;

    void f(int i) {
        x = i;
    }
};

int main(int argc, char **argv)
{
    myClass test;
    test.f(12);
    cout << test.x << endl; // Compiler error trying to access non-const T&
    return 0;
}
Community
  • 1
  • 1
loop
  • 3,460
  • 5
  • 34
  • 57
  • 3
    Shrug. `operatorT&` is private, so there's some code that you haven't shown that's trying to use it. Please post minimal code that's detailed enough to compile and show the error. – Pete Becker Sep 03 '12 at 18:29
  • The `myobj` is declared as `const myclass myobj`, right? – Sergey Kalinichenko Sep 03 '12 at 18:31
  • 3
    This isn't working. The `const` version is used when `myobj` is const. Otherwise the non-const operator is used. Being public or private doesn't matter when selecting the operator. Access is checked afterwards. – Bo Persson Sep 03 '12 at 18:31
  • @BoPersson: This is really an answer, not a comment. – bitmask Sep 03 '12 at 18:38
  • Why do you have the private non-const version (everything with access to that can just as easily work directly on `data`) anyways? – Grizzly Sep 03 '12 at 18:40
  • Alright I edited to show some code. How can I make it so that a const reference is returned publicly and a non const reference returned privately? – loop Sep 03 '12 at 18:40
  • @bitmask - I don't know. It doesn't solve the problem, just telling why the attempt isn't good enough. – Bo Persson Sep 03 '12 at 18:41
  • @Grizzly Say I have a string `s`, and in a private function I call another function that accepts a string as a reference. I was advised in the other question that to do this I have to return a non const reference. – loop Sep 03 '12 at 18:42
  • @test: Your code doesn't even compile. – bitmask Sep 03 '12 at 18:43
  • @bitmask I know, that's because I can't access the non const reference because it's private. I want to know how or even if it's possible to return a const reference publicly and a non const reference privately. – loop Sep 03 '12 at 18:46
  • @test: No, that's not what I meant. `friend C` is illegal; You cannot befriend template parameters. – bitmask Sep 03 '12 at 18:46
  • @bitmask I don't know what to tell you about that. In Visual Studio 2010 it is ok. That line was the answer to the question in the other thread because I thought I'd have to do `friend class C` or something. If there is a better way please let me know! – loop Sep 03 '12 at 18:49
  • @test: Yes, I now got what you're trying to do, let me think... And `friend C` seems to be allowed in C++11 but not ubiquitously supported, yet. So, sorry for my previous comment. – bitmask Sep 03 '12 at 18:55
  • @test: you indeed need a non const reference to do that. However what is stopping you from simply passing `x.data` in that case? – Grizzly Sep 04 '12 at 12:32

3 Answers3

3

Actually, visibility and accesibility are independent things in C++:

  • Visibility rules say that all the member functions are considered when resolving an overload.
  • Accesibility means that if the chosen overload function is not accesible from the used context (private member from outside of the class, for example) a compiler error will happen.

Some people think that private members will not participate in overload resolution if the function in called from outside of the class, but that's not the case. All functions, irrespective of the access, are considered.

About your specific problem, you do:

std::cout << test.x;

Here test is non-const, so is test.x, and of the two overload conversion functions, the non-const one is chosen. But, alas, this function is private, thus compiler error.

The quick solution is to do a const_cast:

std::cout << const_cast<const myClass&>(test).x;

Or if you prefer:

const myClass &ctest = test;
std::cout << ctest.x;

The right solution would be to just remove the non-const private one. You don't need it, since from the class context you can use the data member directly.

Frankly, it looks like you are trying to implement properties in C++ using the syntax from other languages. Properties are fine, but that's not the C++ way.

My advice is: do not fight the language, accept the syntax as it is, and just do the parenthesis thing. Or if x does not hold an invariant, just do it public.

The shortest way would be something like:

class myClass {
private:
    int _x;
public:
    int x() const {
        return _x;
    }
    //if needed
    void x(int value) {
        _x = value;
    }
};

The people that will read your code in the future (mind you, it might be me!) will greatly appreciate that you do not try and reinvent the language.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Thanks. You don't think there is a way to do what I want in C++? Basically I wanted to write doSomething(test.x) outside the class and doSomething(x) inside the class. In either case x is just called x, not _x or getX() etc. – loop Sep 03 '12 at 19:43
  • There are two ways I can think of: 1) write two versions of `doSomething`, one that takes a `pub` and one that takes a `priv` and make `x` have a public conversion operator to `pub` and a private one to `priv`; 2) make `x` public! – rodrigo Sep 03 '12 at 19:53
  • @BenVoigt: Then there is little you can do without nasty hacks. One of the rationales of the C++ rules is that the same syntax should (generally) have the same meaning everywhere. And you want the same expression to mean different things depending on the context. – rodrigo Sep 04 '12 at 17:46
1

In C++, access checks are done after overload resolution. This has both advantages and disadvantages over the other possibility -- removing inaccessible functions from the candidate set.

Usually the solution is to use a different name for the private function. But it appears that you're trying to conform to a particular interface required by some functions you've befriended. I don't think there's an easy workaround for that. Private inheritance won't help in the general case, because inherited functions aren't part of the candidate set, they're hidden by a function in the derived class. But conversion functions are inherited... and after testing, it seems that the original problem recurs (even a private conversion in a private base class is found).

So I finally suggest using a named function instead of a conversion, as in:

template <typename T, typename C>
class proxy
{
    friend C;
private:
    T data;
    T operator=(const T& arg) { data = arg; return data; }
    T& mutate() { return data; }
public:
    operator const T&() const { return data; }
};

class myClass
{
public:
    proxy<int,myClass> x;

    void f(int i)
    {
        x.mutate() = i;
    }
};
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Thanks Ben. I'm looking for something where I can write doSomething(test.x) outside of the class and doSomething(x) inside the class. There doesn't appear to be a way to do this though. I appreciate your suggestion. – loop Sep 03 '12 at 19:41
  • 1
    I realize that, and trying to get as close as C++ allows. `test.x` will work outside the class, and `x` will work inside the class for non-mutating usage. And with operator overloading, you can make `x=i;` and `x++;` work also. But I don't see any way to avoid `changeSomething(x.mutate());` – Ben Voigt Sep 03 '12 at 20:28
0

test is not const, test.x is not const, so myClass::operator int() is a better match that myClass::operator int() const.

Access control (private/public) doesn't enter into it.

Piotr Sobiegraj
  • 1,775
  • 16
  • 26
dave
  • 1