以下代码使用 clang 和 MSVC 成功编译,但在 GCC 6.1.0 中编译失败。
#include <memory>
template<typename R, typename T, typename... Args>
T* test(R(T::*)(Args...) const)
{
return nullptr;
}
int main()
{
using T = std::shared_ptr<int>;
T* p = test(&T::get);
}
并出现以下错误消息
prog.cc: In function 'int main()':
prog.cc:13:16: error: invalid conversion from 'std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2u>*' to 'T* {aka std::shared_ptr<int>*}' [-fpermissive]
T* p = test(&T::get);
~~~~^~~~~~~~~
问题是 libstdc++ 实现了std::shared_ptr
通过继承成员函数get
从基类std::__shared_ptr
.
在C++标准中20.8.2.2 类模板shared_ptr,它指定 std::shared_ptr 类的类定义以及该类的所有成员函数。
我的问题是,实现是否必须至少提供标准类中标准中定义的所有公共类成员?是否允许通过继承自 libstdc++ 中实现的基类来提供成员函数?
该标准对类型及其成员的规范是规范性文本,除非明确地另有说法。因此,需要遵循......在需要遵循的实施范围内anything从标准。
这个程度就是“仿佛”规则。也就是说,只要类型的行为“就像”按照指定完成的那样,实现就可以做它想做的事情。该标准使用特定语言声明类型可以从任意的、提供实现的基类派生的原因是因为这是用户可以检测到的东西。它是可见的行为(通过隐式转换等),因此标准必须例外才能允许它。
继承成员是almost与在主类中声明它相同。事实上,我所知道的区分差异的唯一方法就是按照您在这里所做的操作:使用模板参数推导规则。即使您可以将成员指定为Derived::get
,如果它确实来自某个基类,编译器就会知道。
然而,[member.functions] 在这里拯救了 GCC。它具有显式语言,允许标准库实现向类添加额外的重载。因此,您使用std::shared_ptr<int>::get
这里没有明确定义的行为。事实上,脚注 187 澄清了这一点:
因此,C++标准库中类的成员函数的地址具有未指定的类型。
这只是一个脚注,但意图似乎很明确:您不能依赖任何特定的实现来返回任何特定类型的成员指针。即使您对正确的签名应用了强制转换操作,也不能保证它会起作用。
因此,虽然标准库中的类定义是规范文本,但 [member.functions] 明确表明only关于这些定义,您可以保证的是您可以使用提供的参数调用这些函数。其他任何事情(例如获取成员指针)都是实现定义的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)