确定是否传递了类型的宏
Macro that figures out if a type was passed
我想使用宏制作自己的typeid
。例如,如果我打电话给MY_TYPEID(token)
我希望它解析为
my_type_info<token>()
它是否是一种类型,并且
my_type_info(token)
它是否是一个值。
有什么办法可以在C++做到这一点吗?
这是...艰难。
这是完成的宏:
#define MY_TYPEID(...)
[&](auto...) {
auto &&thing(__VA_ARGS__);
auto probe = [](auto...) -> decltype(thing, void()) {};
if constexpr(detail_type_info::wizz(probe))
return detail_type_info::my_type_info(
std::forward<decltype(thing)>(thing)
);
else
return detail_type_info::my_type_info<
my_decltype(__VA_ARGS__)
>();
}()
这。。。看起来有趣的小玩意依赖于与我的另一个答案相同的基本原理:thing
是转发引用或函数声明,具体取决于参数是表达式还是类型。
thing
是引用的情况很简单(ed(:它最终会作为转发的参数转发到my_type_info
从那里拾取它。
thing
是一个函数的情况很有趣:它有一个推导的返回类型,但尚未(也不会(定义。因此,在提供定义之前,不可能使用它。这种"使用"包括琐碎的使用,例如普通thing;
:只是试图将其放入表达式中会使程序格式不正确。
此特征是通过 SFINAE 层检测的:probe
是一个通用 lambda,其返回类型使用thing
。但由于它是通用的,所以在我们调用lambda 之前,这实际上不会爆炸。这正是detail_type_info::wizz
试图做的:
namespace detail_type_info {
template <class F>
constexpr auto wizz(F probe) -> decltype(probe(), true) { return true; }
constexpr auto wizz(... ) -> decltype( false) { return false; }
}
detail_type_info::wizz(probe)
尝试匹配这些重载之一。第一个重载尝试在未计算的上下文中调用probe
,实例化probe
(lambda(的调用运算符。如果thing
确实在等待推断其返回类型,则此实例化将失败,并且整个重载都将消失。第二个重载不做这样的事情,并且始终有效,但由于...
而从未优先考虑。
所以我们现在有一种方法可以通过detail_type_info::wizz(probe)
来判断宏的参数是类型(false
(还是表达式(true
(。这是由一个if constexpr
打开的,它通过使最外面的lambda成为模板而有效。
还有最后一个障碍:true
分支中的detail_type_info::my_type_info(std::forward<decltype(thing)>(thing))
始终有效(即使在thing
函数声明的情况下如果实例化它会中断(。
然而,false
分支不能以天真的方式调用return detail_type_info::my_type_info<__VA_ARGS__>()
,因为当__VA_ARGS__
是一个不是有效的非类型模板参数(例如double
(的表达式时,这可能会变成无稽之谈,在这种情况下,编译器会立即发出咩咩声。
这就是为什么我在实现my_decltype
的地方重用了我的另一个答案,它在表达式上decltype
,对类型进行无操作,因此始终形成有效的函数调用。
在所有这些机器到位后,并增加了两个my_type_info
存根,如下所示:
namespace detail_type_info {
template <class T>
void my_type_info(T &&) {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template <class T>
void my_type_info() {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
}
int main() {
MY_TYPEID(int);
MY_TYPEID(4.2);
}
。按预期输出:
void detail_type_info::my_type_info() [with T = int]
void detail_type_info::my_type_info(T&&) [with T = double]
完整代码:
namespace detail_typeOrName {
struct probe {
template <class T>
operator T() const;
};
template <class T>
T operator * (T const &, probe);
probe operator *(probe);
}
#define my_decltype(x) decltype((x) * detail_typeOrName::probe{})
namespace detail_type_info {
template <class T>
void my_type_info(T &&) {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template <class T>
void my_type_info() {
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template <class F>
constexpr auto wizz(F probe) -> decltype(probe(), true) { return true; }
constexpr auto wizz(... ) -> decltype( false) { return false; }
}
#define MY_TYPEID(...)
[&](auto...) {
auto &&thing(__VA_ARGS__);
auto probe = [](auto...) -> decltype(thing, void()) {};
if constexpr(detail_type_info::wizz(probe))
return detail_type_info::my_type_info(
std::forward<decltype(thing)>(thing)
);
else
return detail_type_info::my_type_info<
my_decltype(__VA_ARGS__)
>();
}()
科里鲁的现场演示
- 检查 std::shared_ptr<> 的当前底层类型是否为 T
- 检查函数返回类型是否与STL容器类型值相同
- 检查某些类型是否是模板类 std::optional 的实例化
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 检查一个类型是否直接派生自"enable if"上下文中的另一个类型(是其子类型)
- 如何检查模板类型是否可以从给定类型构造
- 用于检测函数类型是否为否的特征
- 了解类型是否可调用
- 通过引用传递参数;函数返回类型是否必须为 VOID?
- 键入特征以检查类型是否可从流和 MSVC 读取
- 从双精度转换为整数的显式类型是否始终检查整数溢出?
- 如何获取类型是否真正可移动可构造
- 在编译时检查类型是否为 std::basic_string<T> C++
- 在C++中,转换为simd类型是否有未定义的行为
- POD类型是否完全等同于琐碎的标准布局类型
- 聚合类型是否意味着它也是标准布局
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- 验证(使用 static_assert)元组类型是否遵循某种顺序(有状态编译时检查)
- 强制转换为不相关的引用类型是否违反严格的别名规则?
- 特征:从数组类型中获取标量类型是否记录?