C++-使用模板公开返回常量引用,私下返回非常量引用

C++ - Using a template to return a const reference publicly and a non const reference privately

本文关键字:返回 引用 常量 非常 C++-      更新时间:2023-10-16

问题是:如何使const引用公开返回,而非const引用私下返回

我正试图为类中的一些变量创建一个只读模板。这涉及到一个模板类,该类在公开时返回对数据的常量引用。然而,在类中,我需要对数据进行操作,因此我试图返回一个私下不const的引用。以下是基本信息:

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

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

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

实际上,可见性可访问性在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;
    }
};

将来阅读你的代码的人(请注意,可能是我!)会非常感激你没有尝试重新发明语言。

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

test不是常量,test.x不是常量,所以myClass::operator int()myClass::operator int() const更匹配。

访问控制(私有/公共)未进入。