【问题标题】:Why is it erroneous to return address of a std::vector element via a const function?为什么通过 const 函数返回 std::vector 元素的地址是错误的?
【发布时间】:2019-03-05 06:03:23
【问题描述】:

我查看了以下线程,但他们没有谈论地址返回函数的constness:

(1)Returning a pointer to a vector element in c++

(2)It's safe to return address of a std::map value?

在代码中:

class test_c{
    private: 
        std::vector <double> dblvec;
        double *dblarray;
    public:
        double *AddVecElem(int index){return &dblvec[index];} //Fine
        double *AddVecElem(int index) const {return &dblvec[index];} //compiler error "Return value type does not match function type"
        double *AddArrElem(int index){return &dblarray[index];}//Fine
        double *AddArrElem(int index) const {return &dblarray[index];}//Fine
};

只有将向量元素的地址作为const 函数返回的情况才会产生编译器错误。

这是为什么?返回向量元素的地址如何影响对象的状态?

【问题讨论】:

  • "返回向量元素的地址如何影响对象的状态?" -- 因为你可以通过指向非常量元素。通过从 const 函数返回 double const* 来解决此问题。
  • @zett42 如果const 函数也返回双精度数组,情况会不会如此?

标签: c++ stdvector


【解决方案1】:

因为dblvec[index]const 函数中会调用:

const_reference operator[]( size_type pos ) const;

至此,&amp;dblarray[index] 的类型为 const double *,并且无法转换为 double *

另一方面,dblarray[index]double,因此您返回 double *

如果您在 const 函数中,请查看问题 What can a 'const' method change? 以获取有关什么是 const 的更多详细信息。

【讨论】:

  • double* AddArrElem(int) const{}double* AddArrElem(int) {} 之间,什么时候应该更喜欢前者而不是后者?
  • @Tryer 目前还不清楚AddVecElem 元素应该做什么。一般来说,您应该避免使用像 double *dblarray 这样的 c 样式数组,而是使用 std 容器。并在您想明确(通过编译器确保)调用该函数不会以任何方式更改对象或其成员时使用const 成员函数。
  • 我同意这一点。但是,我的问题仍然是,double* AddArrElem(int) {} 允许人们对返回的地址做什么,而 double* AddArrElem(int) const {} 则无法做到这一点。
  • @Tryer for double* AddArrElem(int)double* AddArrElem(int) const 对返回的指针可以做什么没有区别,两者都具有相同的类型和相同的常量。它对读者有不同的含义double* AddArrElem(int) const {} 表示调用此函数不会导致任何 - 对于与调用者相关的 - 更改,因此对于调用者来说,它应该就像没有任何改变一样。但是如果你从const 成员函数中返回double *,那么这会阻止编译器检查这是否得到保证。
【解决方案2】:

如果你将函数声明为类 const 函数,这意味着你不能从函数中更改类的变量,你不能返回类变量的地址,除非你保证这个函数返回一个非可变(常量)值。

const 函数内部,该函数将类的变量视为const 变量,因此当您尝试返回地址时,实际上返回的是const double *,而不是函数要求的double *

这种情况的解决方案是返回一个指向const T的指针:

const double* AddVecElem(int index) const {return &dblvec[index];}

或者,正如您提到的,将此函数声明为非常量函数。

另一种解决方案是返回一个非指针值(值的副本),并使用void setValue(..)函数更改该类的变量数据。

至于double *dblarray,您的函数不会返回指针本身,而是返回变量的副本(注意-您的函数返回double*,并且变量在同一指针级别内-因此它返回一个副本)。就像下面的例子:

private:
    int value;
public:
    int return_value() const {
        return value; // Copy of the const returns - you don't change class's variable data
    }
    int* return_pointer_value() const {
        return &value; // Compiler error - expected int* returns const int*
    }

所以,如果你想让指针的情况等于向量的情况,你应该返回double**而不是double*

double **AddArrElem(int index) const {
    return &dblarray; // Compiler error - expected double** returns const double *const*
    // (*const)=> for 'dblarray', (*)=> for '&'
}

那么为什么向量的行为不同呢?为什么不能返回 &dblvec[index]? 正如@t.niese 帖子中提到的,对vector 的operator 函数的调用识别出它是一个const 向量,所以它会自动返回const double- 让我们看看这个函数:

double& AddVecElem(int index) const {
    return dblvec[index]; // Compiler error - Expected double returns const double
}

我们想返回对这个变量的引用,但是返回的变量是一个常量。所以当你想返回double*时,你实际上返回了const double*

即使您尝试四处调用vector.data(),它也会返回const double * 而不是double* - 这就是向量和指针之间的区别。

作为参考:Can const member function return a non-const pointer to a data member?

【讨论】:

  • &amp;dblarray 返回的错误与&amp;dblvec[index] 的错误不同。 &amp;dblarraydouble* const*(对象的成员dblarray 是const,不能更改),&amp;dblvec[index]const double*(向量中的值不能更改)。 return dblarray 等于 dblarray[0] 仍然是可能的。原因是const成员函数只要求dblarray所持有的值(内存地址)不能改变,但是这个限制对于它的内容是不成立的。
  • 感谢您的帖子和努力。这很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多