我有一个用户用来与系统连接的类。这个类使用Pimpl隐藏它的内部,因此它唯一的实际成员是对完成所有工作的真实隐藏对象的引用。
因为类具有引用语义,所以它通常通过值传递,就像指针一样。这会导致const
正确性出现问题。只需将const
值复制到非const
值,即可轻松破解类的const
性质。除了完全阻止复制之外,没有办法避免这种情况。
我希望能够返回这些值的const
值,这会保留对象的const
性质。 没有创建新类或其他东西。
基本上我想阻止这种情况发生:
struct Ref
{
int &t;
Ref(int &_t) : t(_t) {}
};
Ref MakeRef(int &t) { return Ref(t); }
int main()
{
int foo = 5;
const Ref r(foo);
const Ref c(r); //This should be allowed.
Ref other = MakeRef(foo); //This should also be allowed.
Ref bar(r); //This should fail to compile somehow.
return 0;
}
毕竟,如果我直接这样做,它将无法工作:
int &MakeRef(int &t) {return t;}
int main()
{
int foo = 5;
const int &r(foo);
const int &c(r); //This compiles.
int &other = MakeRef(foo); //This compiles.
int &bar(r); //This fails to compile.
return 0;
}
答案 0 :(得分:2)
这与将const T*
和T* const
合并而来的问题完全相同:引用和引用的可变性是截然不同的。对于所有四种可能的组合,C ++中都有有效的用例。我会为“引用T”和“引用const T”制作不同的类型:
#include <type_traits>
template <typename T>
struct Ref
{
T &t;
Ref(T &_t) : t(_t) {}
Ref(const Ref<typename std::remove_cv<T>::type>& other) : t(other.t) {}
};
template <typename T>
Ref<T> MakeRef(T& t) { return {t}; }
template <typename T>
Ref<const T> MakeConstRef(const T& t) { return {t}; }
int main()
{
int foo = 5;
auto r = MakeConstRef(foo);
auto c = r; // This is allowed.
auto other = MakeRef(foo); // This is also allowed.
Ref<const int> baz = other; // This works, too.
Ref<int> bar = c; // This fails to compile, as desired.
}
答案 1 :(得分:0)
你问的是不可能的。这两行不可能表现不同:
const Ref c(r); //This should be allowed.
Ref bar(r); //This should fail to compile somehow.
两行都将通过相同的代码路径执行,它们都将通过相同的复制构造函数(您的或自动生成)执行。唯一的区别是前者会产生一个const最终变量。
不幸的是,即使你设法阻止上述内容在你想要的情况下进行编译,有人也可以简单地执行以下操作来规避你的保护:
const Ref c(r);
Ref &bar = (Ref&)c;
如果你试图阻止其他人为你的班级做些讨厌的事情,你需要找到一个替代方法来使用对局部变量的引用。如果你只是担心自己,那就不要做任何你不应该做的事情:)
答案 2 :(得分:0)
通常,CV修饰符不会从类/结构定义中移过引用或指针。这是因为它不是对象的聚合部分,所以从技术上讲,你并没有真正按照对象行事,只是指向它的东西。
你需要做的就是像这样滚动自己的常量:
struct constRef
{
int const& _t;
constRef(int const& rxo) : _t(rxo) {}
constRef(constRef const& rxo) : _t(rxo._t) {}
int const & t() const { return _t; }
static constRef Make(int const & t) { return t; }
};
struct Ref
{
int& _t;
Ref(int& ro) : _t(ro) {}
Ref(Ref const& rxo) : _t(rxo._t) {}
operator constRef() const { return constRef(_t); }
int& t() { return _t; }
static Ref Make(int& t) { return t; }
};
int main()
{
int foo = 5;
Ref foo2(foo);
constRef r(foo2); // non-const -> const This compiles.
constRef c(r); // const -> const This compiles.
Ref other = Ref::Make(foo); // non-const -> non-const This compiles
Ref bar(r); // const -> non-const This fails to compile
return 0;
}
这允许通过Ref::operator constRef() const
转换函数在非const到const类型之间进行自动单向转换。可以找到工作模型here。
唯一的问题是,对于const对象正确的任何类型的操作都必须重复签名,并且主体指向const类的实现。
解决这个问题的一种可能方法是继承,但这样做可能会变得更加混乱和有问题,更不用说会降低编译器优化的能力。