是否允许标准库实现具有与C++标准不同的类定义?

kwa*_*nti 12 c++ c++-standard-library language-lawyer

以下代码使用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);
}
Run Code Online (Sandbox Code Playgroud)

以下错误消息

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);
            ~~~~^~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

问题是libstdc ++是std::shared_ptr通过get从基类继承成员函数实现的std::__shared_ptr.

在C++标准20.8.2.2类模板shared_ptr中,它指定了std :: shared_ptr类的类定义以及该类的所有成员函数.

我的问题是,实现是否必须至少提供标准类中标准中定义的所有公共类成员?是否允许通过继承libstdc ++中实现的基类来提供成员函数?

Nic*_*las 5

除非明确说明,否则标准的类型及其成员规范是规范性文本.因此,需要实现遵循......在某种程度上要求实现遵循标准中的任何内容.

而那个程度就是"似乎"的规则.也就是说,允许实现执行它想要的操作,只要类型的行为"好像"按指定完成即可.标准具有特定语言的原因表明类型可以从任意的,实现提供的基类派生,因为这是用户可以检测到的.它是可见的行为(通过隐式转换等),因此标准必须进行异常才能允许它.

继承成员几乎与在主类中声明成员相同.实际上,我所知道的唯一方法就是做你在这里做的事情:使用模板参数演绎规则.即使您可以将成员指定为Derived::get,如果它确实来自某个基类,编译器也会知道.

但是,[member.functions]来到GCC拯救.它具有显式语言,允许标准库实现向类添加额外的重载.因此,您在std::shared_ptr<int>::get这里的使用并不是明确定义的行为.实际上,脚注187澄清了这一点:

因此,C++标准库中类的成员函数的地址具有未指定的类型.

这只是一个脚注,但意图似乎很清楚:你不能依赖任何特定的实现来返回任何特定类型的成员指针.即使您对正确的签名应用了强制转换操作,也无法保证它能够正常工作.

因此,虽然标准库中的类定义是规范性文本,但[member.functions]清楚地表明,您可以保证这些定义的唯一内容是您可以使用提供的参数调用这些函数.其他任何东西,比如获取成员指针,都是实现定义的.