Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 安全地返回对成员变量的引用_C++_Templates - Fatal编程技术网

C++ 安全地返回对成员变量的引用

C++ 安全地返回对成员变量的引用,c++,templates,C++,Templates,在我正在设计的库中,我有时需要对类的大型成员变量进行读取访问。由于它们的大小,我不想创建通过复制成员返回的getter。我不希望它们可以从外部修改,因此我不能将它们公开或返回对它们的引用。所以我想我应该用一个“阅读器”: (这并不是真正的双打) 但在这里,有人可以const_cast引用并直接访问数据。即使没有恶意意图,也有人可以保护对数据成员的引用,并在原始对象超出范围后保留它。我知道const引用可以保持一个临时的可行,但这并不能消除const_cast-problem。 所以我想出了一个解

在我正在设计的库中,我有时需要对类的大型成员变量进行读取访问。由于它们的大小,我不想创建通过复制成员返回的getter。我不希望它们可以从外部修改,因此我不能将它们公开或返回对它们的引用。所以我想我应该用一个“阅读器”:

(这并不是真正的双打)

但在这里,有人可以const_cast引用并直接访问数据。即使没有恶意意图,也有人可以保护对数据成员的引用,并在原始对象超出范围后保留它。我知道const引用可以保持一个临时的可行,但这并不能消除const_cast-problem。 所以我想出了一个解决办法:

#include <iostream>

template<class T>
class SafeMemberReference
{
public:
    using type = T;
    SafeMemberReference(const T& t) :t(t) {}
    explicit SafeMemberReference(T&& t) = delete;

    operator const T& () && {return t; }
    T get() && {return t; }
private:
    const T& t;
};

class TestClass
{
public:
    explicit TestClass(double d): d_(d){}

    SafeMemberReference<double> readD() const { return d_; }

private:
    double d_;
};

int main()
{
    TestClass foo(1.2);

    // temporary from read can be used as temporary in expressions
    std::cout << foo.readD() << std::endl;

    // temporary can be used to copy from
    auto x = foo.readD().get();

    // lvalue can not be used, so a possible dangling reference is no problem
    auto ref = foo.readD();
    //std::cout << ref << std::endl;
 }
#包括
模板
类安全成员引用
{
公众:
使用类型=T;
安全成员引用(常量T&T):T(T){
显式安全成员引用(T&&T)=删除;
运算符常量T&(&&{return T;}
T get()&{return T;}
私人:
康斯特T&T;
};
类TestClass
{
公众:
显式TestClass(双d):d_d(d){}
SafeMemberReference readD()常量{return d_;}
私人:
双d_;
};
int main()
{
测试类别foo(1.2);
//“从读取临时”可以用作表达式中的临时

任何试图对抗语言本身的解决方案都不是好的解决方案

如果他们以这种方式使用了
const\u cast
,他们应该得到一个教训:试图通过
const\u cast
在一个最初声明为
const
的对象上更改一个对象的行为是未定义的。即使你设法想出一个解决方案来防止这种情况发生,一个怀有敌意的程序员仍然可以采取行动对象的地址,偏移该地址(使用
无符号字符*
指针算法)并通过该指针修改数据成员

因此,如果我是你,我不会反对这种语言。如果我是你,按照你最初的建议,返回一个
const
引用


代码静态分析工具/编译器警告/代码审查/人力资源部门将帮助您保持其他协作程序员的直线性和狭隘性。

任何试图对抗语言本身的解决方案都不是一个好的解决方案

如果他们以这种方式使用了
const\u cast
,他们应该得到一个教训:试图通过
const\u cast
在一个最初声明为
const
的对象上更改一个对象的行为是未定义的。即使你设法想出一个解决方案来防止这种情况发生,一个怀有敌意的程序员仍然可以采取行动对象的地址,偏移该地址(使用
无符号字符*
指针算法)并通过该指针修改数据成员

因此,如果我是你,我不会反对这种语言。如果我是你,按照你最初的建议,返回一个
const
引用


代码静态分析工具/编译器警告/代码审查/人力资源部门将帮助您保持其他协作程序员的直截了当。

安全成员引用解决了什么问题?SafeMemberReference既没有解决悬空引用的问题,也没有避免不必要的复制,也没有防止修改使用
const\u cast
。也可以直接返回一个副本或
const&
。您有数据要处理。您有一个封装它的类。为什么不像这个类方法那样实现所有需要的数据操作,而不直接访问数据。
SafeMemberReference
解决了什么问题?它也没有解决问题使用
const\u cast
避免不必要的副本或阻止修改的悬空引用的问题。最好直接返回副本或
const&
。您有数据要处理。您有一个封装它的类。为什么不像此类方法那样实现所有需要的数据操作,而无需直接访问到数据。有关何时
const\u cast
可以的更多信息,您可能需要阅读、一个问题和一个问题。出于好奇:在OP的情况下,这真的是未定义的行为吗?如果原始对象是非const(哪个OP的
d
),则表示
const\u cast
是安全的。有关
const_cast
何时可以的更多信息,您可能需要阅读、一个问题和一个问题。出于好奇:OP的情况下这真的是未定义的行为吗?如果原始对象是非常量(哪个OP的
d_
),则表示
const_cast
是安全的。
#include <iostream>

template<class T>
class SafeMemberReference
{
public:
    using type = T;
    SafeMemberReference(const T& t) :t(t) {}
    explicit SafeMemberReference(T&& t) = delete;

    operator const T& () && {return t; }
    T get() && {return t; }
private:
    const T& t;
};

class TestClass
{
public:
    explicit TestClass(double d): d_(d){}

    SafeMemberReference<double> readD() const { return d_; }

private:
    double d_;
};

int main()
{
    TestClass foo(1.2);

    // temporary from read can be used as temporary in expressions
    std::cout << foo.readD() << std::endl;

    // temporary can be used to copy from
    auto x = foo.readD().get();

    // lvalue can not be used, so a possible dangling reference is no problem
    auto ref = foo.readD();
    //std::cout << ref << std::endl;
 }