const correctness and return values - C++

瘦欲@ 提交于 2020-01-06 15:50:54

问题


Please consider the following code.

struct foo
{
};

template<typename T>
class test
{
public:   

    test() {} 

    const T& value() const
    {
        return f;
    }

private:
    T f;
};


int main()
{
    const test<foo*> t;
    foo* f = t.value();
    return 0;
}

t is a const variable and value() is a constant member-function which returns const T&. AFAIK, a const type is not assignable to a non-const type. But how foo* f = t.value(); compiles well. How this is happening and how can I ensure value() can be only assigned to const foo*?

Edit

I found that, this is happening on when templates are used. Following code works as expected.

class test
{
public:   

    test() {} 

    const foo* value() const { return f; }

private:
    foo* f;
};


int main()
{
    const test t;
    foo* f = t.value(); // error here
    return 0;
}

Why the problem is happening when templates are used?


回答1:


Because you have two levels of indirection - in your main function, that call to value returns a reference to a const pointer to a non-const foo.

This can safely be copied into non-const pointer to a non-const foo.

If you'd instantiated test with const foo *, it would be a different story.

const test<const foo*> t;
foo* f = t.value(); // error
const foo* f = t.value(); // fine
return 0;

Update

From the comment:

value() returns const T& which can only be assigned to another const type. But in this case, compiler is safely allowing the conversion.

Const data can only be read. It cannot be written ("mutated"). But copying some data is a way of reading it, so it's okay. For example:

const int c = 5;
int n = c;

Here, I had some const data in c, and I copied the data into a non-const variable n. That's fine, it's just reading the data. The value in c has not been modified.

Now, suppose your foo had some data in it:

struct foo { int n; };

If I have a non-const pointer to one of those, I can modify the n value through the pointer. You asked your test template to store a pointer to a non-const foo, and then made a const instance of test. Only the pointer address is constant, therefore. No one can change the address stored in the pointer inside test, so it cannot be made to point to another object. However, the object it points to can have its contents modified.

Update 2:

When you made your non-template version of the example, you made a mistake. To get it right, you need to substitute foo * into each place where there's a T.

const T& value() const

Notice that you have a reference to a const T there. So the return value will be a reference to something const: a foo *. It's only the pointer address that can't be modified. The object it points to can have its contents modified.

In your second example, you got rid of the reference part, which changes the meaning and makes the const modifier apply to the object that the pointer points to, instead of applying to the pointer itself.




回答2:


Use the following template specialization:

template<typename T>
class test<T*>
{
public:

    test() {}

    const T* value() const
    {
        return f;
    }

private:
    T* f;
};

After including this, g++ says:

d.cpp: In function ‘int main()’:
d.cpp:41: error: invalid conversion from ‘const foo*’ to ‘foo*’



回答3:


There's nothing wrong in your code, having a const reference to a pointer only means that you can't modify the pointer, but the pointed-to object remains perfectly mutable. If inside your main function you try to change the address pointed to by the f member of t you'll see that you can't: encapsulation is perfectly preserved.

This is the same principle that makes the following code valid:

void foo(std::vector<int *> const & v)
{
    *v[0] = 0; // op. [] returns const & to int *
}

People new to C++ are usually surprised by this behavior, because for them a const vector should not allow the modification of its elements. And in fact it doesn't, because the pointer stored in the vector does not change (it keeps pointing to the same address). It's the pointed-to object which is modified, but the vector does not care about that.

The only solution is to do as Amit says and provide a specialization of your class for T*.



来源:https://stackoverflow.com/questions/2303188/const-correctness-and-return-values-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!