访问成员变量中的引用会丢弃 constness
Access to reference in member variable discards constness
我在我的代码中对一个对象做了一个包装,它应该修改对该对象的访问。我选择在这里使用一个对象来进行测试,而不是使用具有相同功能的仿函数。基本上:包装器接收对对象的引用并将所有索引访问转发到对象(在一些可能的操作之后)
现在问题来了:访问器丢弃了被包装对象的常量性。
最小示例
struct Foo
{
std::array<int, 2> data;
const int& operator()(int idx) const{
return data[idx];
}
int& operator()(int idx){
return data[idx];
}
};
struct Bar
{
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
return ref(idx);
}
};
template< typename T >
void test(const T& data){
data(1) = 4;
std::cout << data(1);
}
void main(){
Foo f;
test(f);
// Above call does not compile (as expected)
// (assignment of read-only location)
Bar b(f);
test(b); // This does compile and works (data is modified)
}
声明 Bar 的 ()-运算符(包装器)"const",我希望所有成员都可以访问 "const"。所以不应该 return 一个 "int&" 而只能 "const int&"
然而 gcc4.7 愉快地编译了代码并且 const 被忽略了。这是正确的行为吗?这是在哪里指定的?
编辑:
在相关问题上:如果在 Foo 中使用 typedef,例如:
struct Foo
{
using Ref = int&;
using ConstRef = const int&; //1
using ConstRef = const Ref; //2
int* data; // Use int* to have same issue as with refs
ConstRef operator()(int idx) const{
return data[idx]; // This is possible due to the same "bug" as with the ref in Bar
}
Ref operator()(int idx){
return data[idx];
}
};
我注意到 //1 确实按预期工作但 //2 没有。 Return 值仍然可以修改。他们不应该是一样的吗?
是的,这是正确的行为。 ref
的类型是 Foo &
。将 const
添加到引用类型 1 什么都不做——无论如何,引用已经是不可变的了。就像有一个成员int *p
。在 const
成员函数中,其类型被视为 int * const p
,而不是 int const * p
.
您需要做的是在 const
重载中手动添加 const
,如果您需要的话:
struct Bar
{
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
return const_cast<const Foo&>(ref)(idx);
}
};
解决问题编辑:不,typedef 不一样。 const int &
是对(常量 int
)的引用。 const Ref
是一个常数Ref
,即常数(参考int
);数学意义上的括号。
1 我说的是引用类型本身。不要与将 const
添加到引用所指的类型相混淆。
是的,这是预期的行为。原因是您的方法的 const 只表示引用不会更改,而不是引用的对象。引用始终不变,因此它始终为真。用指针看一下这段代码:
int i;
struct Bar
{
int* pi;
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
*pi = 4; // we can change pointed object
pi = &i; // Compile error: we can't change the pointer.
return ref(idx);
}
};
我在我的代码中对一个对象做了一个包装,它应该修改对该对象的访问。我选择在这里使用一个对象来进行测试,而不是使用具有相同功能的仿函数。基本上:包装器接收对对象的引用并将所有索引访问转发到对象(在一些可能的操作之后)
现在问题来了:访问器丢弃了被包装对象的常量性。
最小示例
struct Foo
{
std::array<int, 2> data;
const int& operator()(int idx) const{
return data[idx];
}
int& operator()(int idx){
return data[idx];
}
};
struct Bar
{
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
return ref(idx);
}
};
template< typename T >
void test(const T& data){
data(1) = 4;
std::cout << data(1);
}
void main(){
Foo f;
test(f);
// Above call does not compile (as expected)
// (assignment of read-only location)
Bar b(f);
test(b); // This does compile and works (data is modified)
}
声明 Bar 的 ()-运算符(包装器)"const",我希望所有成员都可以访问 "const"。所以不应该 return 一个 "int&" 而只能 "const int&"
然而 gcc4.7 愉快地编译了代码并且 const 被忽略了。这是正确的行为吗?这是在哪里指定的?
编辑: 在相关问题上:如果在 Foo 中使用 typedef,例如:
struct Foo
{
using Ref = int&;
using ConstRef = const int&; //1
using ConstRef = const Ref; //2
int* data; // Use int* to have same issue as with refs
ConstRef operator()(int idx) const{
return data[idx]; // This is possible due to the same "bug" as with the ref in Bar
}
Ref operator()(int idx){
return data[idx];
}
};
我注意到 //1 确实按预期工作但 //2 没有。 Return 值仍然可以修改。他们不应该是一样的吗?
是的,这是正确的行为。 ref
的类型是 Foo &
。将 const
添加到引用类型 1 什么都不做——无论如何,引用已经是不可变的了。就像有一个成员int *p
。在 const
成员函数中,其类型被视为 int * const p
,而不是 int const * p
.
您需要做的是在 const
重载中手动添加 const
,如果您需要的话:
struct Bar
{
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
return const_cast<const Foo&>(ref)(idx);
}
};
解决问题编辑:不,typedef 不一样。 const int &
是对(常量 int
)的引用。 const Ref
是一个常数Ref
,即常数(参考int
);数学意义上的括号。
1 我说的是引用类型本身。不要与将 const
添加到引用所指的类型相混淆。
是的,这是预期的行为。原因是您的方法的 const 只表示引用不会更改,而不是引用的对象。引用始终不变,因此它始终为真。用指针看一下这段代码:
int i;
struct Bar
{
int* pi;
Foo& ref;
Bar(Foo& r):ref(r){}
int& operator()(int idx) const{
*pi = 4; // we can change pointed object
pi = &i; // Compile error: we can't change the pointer.
return ref(idx);
}
};