从“foo <t>”转换为“const foo <const t =”“>” - C ++ </const> </t>

时间:2010-02-18 16:54:23

标签: c++ templates constants const-correctness

我有一个类似的功能(请不要关心通过引用临时返回。这只是解释问题的一个例子),

const foo<const int>& get_const()
{
    foo<int> f;
    return f;
}

这显然不会编译。我正在寻找一种方法来确保来电者不会更改T的{​​{1}}。我该如何确保?

我已经看到了foo的类似行为。 boost::shared_ptr可转换为shared_ptr<T>。我无法弄清楚它是如何做到的。

任何帮助都会很棒。

4 个答案:

答案 0 :(得分:9)

编译器将foo<T>foo<const T>视为两个完全不同且不相关的类型,因此foo类需要明确支持此类,就像任何其他转换一样。如果您可以控制foo类,则需要提供复制构造函数或隐式转换运算符(或两者)。

template<typename T>
class foo 
{
 public: 

   // Regular constructor
   foo(T t) : t(t) {}

   // Copy constructor (works for any type S convertable to T, in particular S = non-const T if T is const)
   // Remember that foo<T> and foo<S> are unrelated, so the accessor method must be used here
   template<typename S> foo (const foo<S>& copy) : t(copy.getT()) {}

   // Accessor
   T getT() const { return t; }

   // Conversion operator
   operator foo<const T> () const { return foo<const T>(t); }

 private:

   T t;
};

答案 1 :(得分:1)

假设Foo定义如下:

template<typename T> class Foo
{
public:
    Foo(const T& value) : m_value(value) { }
    const T& getValue() const { return m_value; }
    void setValue(const T& value) { m_value = value; }
private:
    T m_value;
};

然后,为了确保Foo的客户端不修改m_value(我认为这就是“我正在寻找一种方法来确保调用者不会改变f的T”),你需要to const限定Foo对象而不是它的模板参数,即

Foo<int> x(1);
x.setValue(2); // OK
const Foo<int> y(1);
y.setValue(2); // does not compile

因此,您的get_foo函数应返回const Foo<T>&,而不是const Foo<const T>&

这是一个完整的,可编辑的例子:

#include <iostream>

template<typename T> class Foo
{
public:
    Foo(const T& value) : m_value(value) { }
    const T& getValue() const { return m_value; }
    void setValue(const T& value) { m_value = value; }
private:
    T m_value;
};

template<class T> class Owner
{
public:
    Owner(const T& value) : m_foo(value) { }
    Foo<T>& getFoo() { return m_foo; }
    const Foo<T>& getConstFoo() const { return m_foo; }

private:
    Foo<T> m_foo;
};


int main(int argc, char** argv)
{
    Owner<int> x(1);
    x.getFoo().setValue(2);
    // x.getConstFoo().setValue(3); // will not compile
}

答案 2 :(得分:0)

如果我没弄错,boost::shared_ptr实现有一个非显式构造函数,它将const T&引用作为参数,然后在RHS的指针上使用const_cast来删除const,允许它们之间的隐式转换。

这样的事情:

shared_ptr(const shared_ptr<const T>& r) : ptr(const_cast<T*>(r.ptr)) {}

这就是你要找的东西吗?

答案 3 :(得分:0)

首先,你通过引用返回一个本地对象......这不好。

foo和foo是两种不同的类型,所以你必须编写代码(转换构造函数)来显式转换它们。

要想得到你想要的东西,请考虑一下:

template <typename T>
struct foo {T* t;};

const foo<int>& get_const(const foo<int>& f) {
    return f;
}

foo<int> f;
const foo<int>& cf = get_const(f);
f.t = 0; // ok, f is not const
*cf.t = 0; // ok because cf.t is const but what cf.t points to is not
cf.t = 0; // compiler error cf.t is const and cannot be lvalue

foo<int>& cf = get_const(f); // compiler error, cannot convert non-const to const without const_cast

如果你正确地完成了封装并且只使用const getter和非const setter访问成员,那么这对你来说已经足够了。记住,如果人们真的想要改变你的对象,他们总是可以const_cast。正确性只是为了捕捉无意识的错误。