将成员功能更改为稳定破坏代码
Change member function to const silently breaks code
我正在为第三方库写一个接口。它通过基本上是void*
的C接口来操纵对象。这是简化的代码:
struct LibIntf
{
LibIntf() : opaquePtr{nullptr} {}
operator void *() /* const */ { return opaquePtr; }
operator void **() { return &opaquePtr; }
void *opaquePtr;
};
int UseMe(void *ptr)
{
if (ptr == (void *)0x100)
return 1;
return 0;
}
void CreateMe(void **ptr)
{
*ptr = (void *)0x100;
}
int main()
{
LibIntf lib;
CreateMe(lib);
return UseMe(lib);
}
一切都很好,直到我在operator void *()
线上添加const
。然后,代码默默默认为使用operator void **()
破坏代码。
我的问题是为什么?
我正在通过不修改对象的函数返回指针。应该能够将其标记为const
。如果将其更改为const
指针,则编译器应该错误,因为operator void **()
不应该是只需void *
的函数匹配的匹配。
这是标准所说的应该发生的事情,但这远非显而易见。对于快速读者,跳到"如何修复它?"最后。
了解为什么const
重要
添加const
预选赛后,当您使用LibIntf
实例调用UseMe
时,编译器将具有以下两个可能性:
-
LibIntf
→ 1LibIntf
→ 2void**
→ 3void*
(通过operator void**()
) -
LibIntf
→ 3const LibIntf
→ 2void*
→ 1void*
(通过operator void* const()
)
1)无需转换。
2)用户定义的转换操作员。
3)法律转换。
这两个转换路径是合法的,所以选择哪一个?
定义C 答案的标准:
[over.match.best]/1
定义
ICSi(F)
如下:
- [...]
- 令
ICSi(F)
表示隐式转换序列,该顺序将列表中的i
参数转换为可行函数的i
TH参数F。[over.best.ics]
定义了隐式转换序列,[over.ics.rank]
定义了对一个隐式转换序列的含义,比另一个更好的转换顺序或更差的转换顺序。鉴于这些定义,可行函数
F1
被定义为 与其他可行函数F2
更好的功能,如果所有参数i
,ICSi(F1)
不比ICSi(F2)
更差的转换序列,然后是
对于某些参数
j
,ICSj(F1)
是比ICSj(F2)
更好的转换序列,或者,如果不是,上下文是用户定义的转换的初始化(请参阅
[dcl.init]
,[over.match.conv]
和[over.match.ref]
),并且是从F1
的返回类型到目标类型的标准转换序列(即,实体的类型是初始化的实体类型)是从F2
的返回类型到目标类型的标准转换序列更好的转换序列。
(我必须在获得几次之前阅读几次。)
这一切都意味着在您的特定情况下 比选项#1比选项#2更好更好,因为对于用户定义的转换操作员,返回类型的转换(void**
)在选项#1中的void*
)在之后考虑参数类型的转换( LibIntf
to CC_46 to const LibIntf
in Option#2中)。
在链中,这意味着在选项#1中没有任何转换(后者在转换链中,但尚未考虑),但是在选项#2中,需要从non-const
转换为const
。选项#1因此被称为更好。
如何修复它?
只需删除将non-const
考虑到const
转换 by casting const
(显式(始终称为转换(或称为转换))):):
struct LibIntf
{
LibIntf() : opaquePtr{nullptr} {}
operator void *() const { return opaquePtr; }
operator void **() { return &opaquePtr; }
void *opaquePtr;
};
int UseMe(void *ptr)
{
if (ptr == (void *)0x100)
return 1;
return 0;
}
void CreateMe(void **ptr)
{
*ptr = (void *)0x100;
}
int main()
{
LibIntf lib;
CreateMe(lib);
// unfortunately, you cannot const_cast an instance, only refs & ptrs
return UseMe(static_cast<const LibIntf>(lib));
}
- 删除C++继承中虚拟类成员的代码重复
- 如何定义一个没有重复代码的继承的 const 类成员函数?
- 为什么第一个代码块产生垃圾值,而第二个代码块将类成员的值相加?
- 标准对此指向成员函数类型模板参数有何说明?是我的代码有误,还是 MSVS 16.6 有问题?
- 避免易失性和非易失性成员函数的代码重复
- 错误:类没有名为"ThisW"的成员;你是说"这个"吗?但是代码没有说"ThisW",它已经说了"This"
- 重构类:无法将派生类中成员函数的公共代码移回基类
- 如何避免大多数成员功能相同的代码重复
- 我写了一个类,它总是将其函数参数委托给它的成员,我该如何改进代码
- 从C++代码中直接读取成员属性
- 如何避免将 const 和非 const 成员函数输入到模板中的代码重复
- s.compare 成员函数在下面的C++代码中的行为如何?
- 如何基于构造函数参数模板化类成员函数的代码
- 如何在析构函数中执行一些代码*在*成员被销毁之后
- 如何使用 gcc 内联汇编器代码访问成员变量
- 带有布尔数组成员的 C++ 结构.如何在主代码中使用
- 如何打包结构的成员以实现高效的跨平台代码
- 设备代码中的CUDA类静态成员的成语
- 在通用模板代码中初始化聚合成员
- C函数调用C 成员函数 - 其中C代码与C编译器编译