1

问题:我怎样才能使一个 const 引用公开返回,一个非 const 引用私下返回?

我正在尝试为我的类中的某些变量创建一个只读模板。这涉及一个模板类,它在公共时返回对数据的 const 引用。但是在课堂上我需要对数据进行操作,所以我试图私下返回一个不是 const 的引用。这是基础知识:

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

当我添加如上所示的非常量引用时,如果我尝试公开访问变量,我的 Visual Studio 2010 cl.exe 编译器告诉我它无法访问类中的私有成员。像cout << myobj.x << endl使用模板声明 x 这样简单的事情将会失败。

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

这是另一个可供参考的线程:
C++ - 如何在 Visual Studio 2010 中制作只读类成员变量 - 代码日志

编辑:您要求提供代码,所以在这里。

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;
}
4

3 回答 3

2

实际上,可见性和可访问性在 C++ 中是独立的

  • 可见性规则说在解决重载时会考虑所有成员函数。
  • 可访问性意味着如果所选的重载函数不能从使用的上下文(例如,来自类外部的私有成员)访问,则会发生编译器错误。

有些人认为如果从类外部调用函数,私有成员将不会参与重载决议,但事实并非如此。所有功能,无论访问权限如何,都被考虑在内。

关于您的具体问题,您可以:

std::cout << test.x;

这里test是非常量,所以也是test.x,并且在两个重载转换函数中,选择非常量的一个。但是,唉,这个函数是私有的,因此编译器错误。

快速的解决方案是做一个const_cast

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

或者,如果您愿意:

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

正确的解决方案是删除非常量私有的。您不需要它,因为从类上下文中您可以data直接使用该成员。

坦率地说,您似乎正在尝试使用其他语言的语法在 C++ 中实现属性。属性很好,但这不是 C++ 的方式。

我的建议是:不要与语言抗争,按原样接受语法,只做括号的事情。或者,如果x不持有不变量,则将其公开。

最短的方法是这样的:

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

将来阅读您的代码的人(请注意,可能是我!)将非常感谢您不尝试重新发明语言。

于 2012-09-03T19:18:30.033 回答
1

在 C++ 中,访问检查是在重载决议之后完成的。与另一种可能性相比,这既有优点也有缺点——从候选集中删除不可访问的函数。

通常解决方案是为私有函数使用不同的名称。但您似乎正在尝试符合您所熟悉的某些功能所需的特定界面。我不认为有一个简单的解决方法。私有继承在一般情况下无济于事,因为继承的函数不是候选集的一部分,它们被派生类中的函数隐藏。但是转换函数是继承的……经过测试,原来的问题似乎又出现了(甚至发现了私有基类中的私有转换)。

所以我最后建议使用命名函数而不是转换,如:

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;
    }
};
于 2012-09-03T19:19:10.500 回答
0

testis not const, test.xis not const, 所以myClass::operator int()更好的匹配是myClass::operator int() const.

访问控制(私人/公共)不参与其中。

于 2012-09-03T18:47:51.207 回答