【问题标题】:const correctness and return values - C++const 正确性和返回值 - C++
【发布时间】:2010-02-20 18:33:53
【问题描述】:

请考虑以下代码。

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 是一个const 变量,value() 是一个返回const T&amp; 的常量成员函数。 AFAIK,const 类型不能分配给非常量类型。但是foo* f = t.value(); 如何编译得很好。这是怎么发生的?如何确保value() 只能分配给const foo*

编辑

我发现,这是在使用模板时发生的。以下代码按预期工作。

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

为什么在使用模板时会出现问题?

【问题讨论】:

    标签: c++ constants const-correctness


    【解决方案1】:

    因为您有两个间接级别 - 在您的主函数中,对 value 的调用返回对指向非 const foo 的 const 指针的引用。

    这可以安全地复制到指向非常量foo的非常量指针中。

    如果您将test 实例化为const foo *,那就另当别论了。

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

    更新

    来自评论:

    value() 返回 const T& 可以 只能分配给另一个 const 类型。但在这种情况下,编译器是 安全地允许转换。

    只能读取常量数据。它不能被写入(“变异”)。但是复制一些数据是一种读取方式,所以没关系。例如:

    const int c = 5;
    int n = c;
    

    在这里,我在c 中有一些 const 数据,我将这些数据复制到了一个非常量变量 n 中。没关系,它只是读取数据。 c 中的值没有被修改。

    现在,假设您的 foo 中有一些数据:

    struct foo { int n; };
    

    如果我有一个指向其中之一的非常量指针,我可以通过指针修改n 值。您要求您的 test 模板存储指向非 const foo 的指针,然后创建 test 的 const 实例。因此,只有指针地址是恒定的。没有人可以更改存储在test 内部的指针中的地址,因此无法使其指向另一个对象。但是,它指向的对象可以修改其内容。

    更新 2:

    当您制作示例的非模板版本时,您犯了一个错误。为了正确,您需要将foo * 替换为每个有T 的地方。

    const T& value() const
    

    请注意,您在此处引用了 const T。所以返回值将是对某个 const 的引用:afoo *。只有指针地址不能修改。它指向的对象可以修改其内容。

    在您的第二个示例中,您去掉了引用部分,这改变了含义并使const 修饰符应用于指针指向的对象,而不是应用于指针本身。

    【讨论】:

    • 谢谢。但我还是不够清楚。 IMO 意见,value() 返回 const T&amp; 只能分配给另一个 const 类型。但在这种情况下,编译器安全地允许转换。同样在我的情况下,我无法制作test&lt;const foo*&gt;
    • @Appu 这是指向 const 的指针和指向非 const 的 const 指针之间的区别。不知道为什么你不能“制造”test&lt;const foo*&gt;
    • 请查看编辑。我有一个非常量成员 f 并从中返回 const。在那种情况下,编译正确地抱怨。使用模板时的行为似乎有所不同。
    • @Appu:当你用foo* 实例化你的模板时,const T&amp; 就是const (foo *)&amp;——一个指向非 const 指针的 const 引用。 reference 是 const,而不是 pointer。您的非模板示例使用不同的类型,const foo *
    • 啊,我明白了。谢谢你的详细解释。对此,我真的非常感激。那么你认为指针类型的部分特化是解决这个问题的方法吗?
    【解决方案2】:

    使用以下模板特化:

    template<typename T>
    class test<T*>
    {
    public:
    
        test() {}
    
        const T* value() const
        {
            return f;
        }
    
    private:
        T* f;
    };
    

    在包含这个之后,g++ 说:

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

    【讨论】:

      【解决方案3】:

      您的代码没有任何问题,对指针的 const 引用仅意味着您不能修改指针,但指向的对象仍然是完全可变的。如果在main 函数中尝试更改tf 成员指向的地址,您会发现不能:完全保留了封装。

      这与使以下代码有效的原理相同:

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

      刚接触 C++ 的人通常会对这种行为感到惊讶,因为对他们来说 const 向量不应该允许修改其元素。事实上它不会,因为存储在向量中的指针不会改变(它一直指向同一个地址)。修改的是指向的对象,但向量并不关心。

      唯一的解决方案是按照 Amit 所说的去做,并为 T* 提供专业化课程。

      【讨论】:

        猜你喜欢
        • 2010-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-13
        相关资源
        最近更新 更多