扩充命名空间 std

来自cppreference.com
< cpp‎ | language
 
 
C++ 语言
 

std 添加声明

namespace stdstd 中嵌套的任何命名空间添加声明或定义,除了下面提到的少数例外,都是未定义行为:

#include <utility>
namespace std {
    // 向命名空间 std 添加函数:未定义行为
    pair<int, int> operator+(pair<int, int> a, pair<int, int> b) {
        return {a.first+b.first, a.second+b.second};
    }
}

添加模板特化

类模板

对于任何标准库类模板的模板特化,仅当其声明依赖于至少一个程序定义类型,且该特化满足原模板的所有要求时,才允许向命名空间 std 中添加,除非这种特化被禁止。

// 获取主 std::hash 模板的声明。不容许我等自己声明它。
// 保证 <typeindex> 提供这种声明,包含它比 <functional> 低廉很多。
 
#include <typeindex> 
 
// 特化 std::hash 使得能以 MyType 为
// std::unordered_set 和 std::unordered_map 中的键
namespace std {
    template <> struct hash<MyType> {
      std::size_t operator()(const MyType& t) const { return t.hash(); }
    };
}
  • floatdoublelong double 以外的类型特化 std::complex 是未指明的。
  • std::atomic 的特化必须拥有被删除的复制构造函数,被删除的复制赋值运算符,及一个 constexpr 值构造函数。
(C++11 起)
  • std::istreambuf_iterator 的特化必须有平凡复制构造函数、constexpr 默认构造函数,及平凡析构函数。
(C++17 前)

声明标准库类或类模板的任何成员类模板的完全或部分特化是未定义行为。

函数模板与模板的成员函数

对于任何标准库函数模板的模板特化,仅当其声明依赖于至少一个程序定义类型,且该特化满足原模板的所有要求时,才允许向命名空间 std 中添加,除非这种特化被禁止。

(C++20 前)

声明任何标准库函数模板的完全特化是未定义行为。

(C++20 起)

声明标准库类或类模板的任何成员函数模板的完全特化是未定义行为

声明标准库类模板的任何成员函数的完全特化是未定义行为

变量模板

声明任何标准库变量模板的完全特化或部分特化是未定义行为,除了显式允许的变量模板。

(C++14 起)
(C++20 起)

模板的显式实例化

对于标准库中定义的 (C++20 起)模板的显式实例化,仅当其声明依赖于至少一个程序定义类型的名称,且实例化满足标准库对原始模板的要求时,允许进行显示实例化。

程序定义类型

程序定义特化是既非标准库的一部分亦非实现所定义的显式模板特化或部分特化。

程序定义类型是既非标准库的一部分亦非实现所定义的非闭包类类型枚举类型,或非实现所提供的 lambda 表达式的闭包类型 (C++11 起),或程序定义特化的实例化。

其他限制

不可将命名空间 std 声明为 inline 命名空间。

可取址函数

若 C++ 程序显式或隐式形成指向标准库函数或标准库函数模板实例化的指针、引用(对于自由函数和静态成员函数)或成员指针(对于非静态成员函数),则程序行为未指明(可能非良构),除非该函数被指定为可取址函数

标准库中仅有的可取址函数是作为函数(或函数模板实例化),接收一个流对象的引用作为其唯一实参的 I/O 操纵符,如 std::endlstd::boolalpha

下列代码在 C++17 中有恰当定义,但从 C++20 起导致未指明行为并且可能无法编译:

#include <cmath>
#include <memory>
int main()
{
    auto fptr0 = &std::betaf; // 通过一元 operator&
    auto fptr1 = std::addressof(std::betal) // 通过 std::addressof
    auto fptr2 = std::riemann_zetaf; // 通过函数到指针隐式转换
    auto &fref = std::riemann_zetal; // 形成引用
}
(C++20 起)