如何在编译期禁止使用 std::string 实例化类模板?
先去掉 T 的引用和 cv 限定,再判断得到的类型是否为 std::string。C++20 可以用 requires 约束模板实参;C++11/14/17 则可以在类内使用 static_assert,在实例化类定义时触发编译错误。
C++20:使用 requires 约束模板
#include <concepts>
#include <string>
#include <type_traits>
template <typename T>
requires (!std::same_as<std::remove_cvref_t<T>, std::string>)
struct AVLTree {
// 类的实现
};
int main()
{
AVLTree<int> tree; // 通过此约束
// 以下声明均因不满足约束而编译失败:
// AVLTree<std::string> a;
// AVLTree<const std::string> b;
// AVLTree<std::string&> c;
// AVLTree<const std::string&> d;
// AVLTree<std::string&&> e;
}
这个约束分两步判断:
std::remove_cvref_t<T>去掉引用以及顶层的const、volatile限定。std::same_as<..., std::string>判断处理后的类型是否与std::string相同,前面的!表示排除相同的情况。
例如,const std::string& 处理后得到 std::string,因此不满足约束。std::remove_cvref_t 和 std::same_as 都需要 C++20。
去掉引用和 cv 限定只用于约束判断,类内部的 T 仍是调用者传入的原始类型。
C++11/14/17:使用 static_assert
项目还未使用 C++20 时,可以组合 std::remove_reference、std::remove_cv 和 std::is_same:
#include <string>
#include <type_traits>
template <typename T>
struct AVLTree {
using UnqualifiedT =
typename std::remove_cv<
typename std::remove_reference<T>::type
>::type;
static_assert(
!std::is_same<UnqualifiedT, std::string>::value,
"AVLTree<T> does not support std::string, including cv/ref variants"
);
// 类的实现
};
int main()
{
AVLTree<int> tree;
// 实例化类定义时触发 static_assert:
// AVLTree<std::string> a;
// AVLTree<const std::string&> b;
}
这里要先去掉引用,再去掉 cv 限定。对于 const std::string&,const 限定的是被引用的对象,直接使用 std::remove_cv 无法去掉被引用类型上的 const。
两种写法的检查时机不同
类内的 static_assert 要等到实例化类定义时才会触发。仅声明指针通常不要求类是完整类型,因此使用上述 static_assert 版本时,下面的声明可以成立:
AVLTree<std::string>* ptr = nullptr;
如果随后定义 AVLTree<std::string> 对象,或者对该类型使用 sizeof,就会触发断言。
使用 requires 时,形成相应的模板特化类型就需要满足约束。因此在 C++20 版本中,上面的指针声明也会因不满足约束而编译失败。如果希望连 AVLTree<std::string>* 这样的类型用法也被拒绝,可以选择 requires。
禁止范围有哪些
上述代码判断的是去掉引用和 cv 限定后的类型是否与 std::string 相同:
std::string及其const、volatile、左值引用、右值引用组合都会被拒绝。using Text = std::string;这样的类型别名也会被拒绝,因为它没有创建新类型。std::string*、std::string_view、std::wstring以及从std::string派生的类型不在禁止范围内。
std::remove_cvref_t 不会去掉指针,也不会把所有字符串相关类型都转换成 std::string。这个判断适用于只需禁止题目指定的类型及其 cv/ref 变体的情况。
备注:内容仅供参考。
