std::is_nothrow_copy_constructible 是c++11引入的编译期类型特征,用于判断类型t是否具有不抛异常的拷贝构造函数,其值为std::integral_constant特化,依赖拷贝构造是否显式声明或隐式推导为noexcept。

std::is_nothrow_copy_constructible 是什么类型特征
std::is_nothrow_copy_constructible 是 C++11 引入的类型特征(type trait),用于在编译期判断某个类型 T 是否具有**不抛异常的拷贝构造函数**。它不是运行时检查,也不调用任何构造函数;它只依赖于该类型的拷贝构造函数是否被显式声明为 noexcept,或是否能被隐式推导为 noexcept(例如所有子对象的拷贝构造都 noexcept)。
它的值是 std::integral_constant<bool value></bool> 的特化,所以实际使用中常取 ::value 或更现代的 ::value_v(C++17 起)。
怎么写一个能通过 is_nothrow_copy_constructible 的类
关键不在“有没有拷贝构造函数”,而在于“它是否被标记或推导为 noexcept”。哪怕你没写拷贝构造函数,只要编译器生成的默认版本满足条件,也会返回 true。
- 显式声明:写
T(const T&) noexcept—— 最稳妥 - 隐式满足:所有成员和基类的拷贝构造函数都
noexcept,且没有用户定义的拷贝构造(让编译器合成) - 注意:哪怕拷贝构造函数体里没 throw,但没加
noexcept且未被推导成功,std::is_nothrow_copy_constructible_v<t></t>就是false
示例:
struct Good {
int x;
std::string s; // std::string 的拷贝构造不是 noexcept(C++11/14 中)
};
static_assert(!std::is_nothrow_copy_constructible_v<good>); // true
struct Better {
int x;
// 不含可能抛异常的成员
};
static_assert(std::is_nothrow_copy_constructible_v<better>); // true(默认合成且推导成功)
</better></good>
常见误判场景:为什么明明写了 noexcept 还是 false
最典型的情况是:类中有成员的拷贝构造函数不是 noexcept,导致整个类的隐式拷贝构造无法被推导为 noexcept,即使你手动加了 noexcept 声明,也可能因违反 ODR 或语义冲突被忽略。
-
std::vector、std::string(旧标准)、std::shared_ptr等容器/智能指针的拷贝构造在 C++11/14 中通常不是noexcept;C++17 起部分已改为noexcept,但需查具体标准版本和实现 - 继承了非
noexcept拷贝构造的基类,且未显式重写并标注noexcept - 声明了
noexcept但定义在别处,而定义里实际抛了异常(编译期仍认为是noexcept,但运行时会调用std::terminate;不过is_nothrow_*只看声明)
验证方式很简单:
static_assert(std::is_nothrow_copy_constructible_v<:string>); // C++17 起通常 true,C++14 通常 false </:string>
在模板和 SFINAE 中怎么安全使用它
它常用于约束模板参数,比如实现一个只接受可无异常拷贝类型的容器操作。但要注意:直接用 static_assert 会硬报错,而用 std::enable_if_t 或 requires(C++20)才能做优雅约束。
- C++17 推荐用变量模板 +
if constexpr分支:
template<typename t>
void copy_if_safe(T const& src) {
if constexpr (std::is_nothrow_copy_constructible_v<t>) {
// 直接构造,无需 try/catch
T dst{src};
} else {
// 降级处理,或抛出 wrapper 异常
throw std::runtime_error("copy may throw");
}
}
</t></typename>
- 避免在非模板上下文中对未定义类型使用,否则可能触发未定义行为(如对不完整类型求值)
真正容易被忽略的是:这个 trait 对 const T&、T&& 等引用类型返回 true(因为引用本身拷贝是 trivial 且 noexcept),但它检查的是 T 本身的拷贝构造能力——别拿引用类型去“绕过”检查,那没意义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











