能否在编译时检查模板类型是否已实例化,以便我可以在enable_if特化中使用这些信息?
假设我有以下代码:
template <typename T> struct known_type { };
如果在编译时实例化了一种已知的类型,我是否可以定义某个 is_known_type(其值为 true)?
能否在编译时检查模板类型是否已实例化,以便我可以在enable_if特化中使用这些信息?
假设我有以下代码:
template <typename T> struct known_type { };
如果在编译时实例化了一种已知的类型,我是否可以定义某个 is_known_type(其值为 true)?
constexpr
的地方使用或不使用的事实,并且你可以查询每个候选项的状态,那么就有可能做到这一点。具体来说,在我们的情况下,没有定义的constexpr
不能作为常量表达式传递,而noexcept
是一个常量表达式的保证。因此,noexcept(...)
返回true
表示存在正确定义的constexpr
。
本质上,这将constexpr
视为是/否开关,并在编译时引入状态。
请注意,这几乎是一种hack,你需要针对特定编译器找到解决方法(请参阅后面的文章),而这个特定的基于friend
的实现可能被未来的标准修订认为是非法的。
说了这些...
用户Filip Roséen在他的文章中专门介绍了这个概念。
他的示例实现如下,带有引用的解释:constexpr int flag (int);
template<class Tag>
struct writer {
friend constexpr int flag (Tag) {
return 0;
}
};
writer是一个类模板,实例化后将在其周围的命名空间中创建一个函数定义(具有签名int flag(Tag),其中Tag是模板参数)。
如果我们再次将constexpr函数视为对某个变量的句柄,我们可以将writer的实例化视为对在friend-declaration中函数背后可用值的无条件写入。
template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };
如果你认为dependent_writer看起来像一个毫无意义的间接引用,我不会感到惊讶;为什么不直接在需要使用writer的地方实例化它,而是通过dependent_writer呢?
- writer的实例化必须依赖于某些东西,以防止立即实例化,并且;
- dependent_writer用于可以使用bool类型作为依赖关系的上下文中。
template<
bool B = noexcept (flag (0)),
int = sizeof (dependent_writer<B>)
>
constexpr int f () {
return B;
}
The above might look a little weird, but it's really quite simple;
- will set B = true if flag(0) is a constant-expression, otherwise B = false, and;
- implicitly instantiates dependent_writer (sizeof requires a completely-defined type).
The behavior can be expressed with the following pseudo-code:
IF [ `int flag (int)` has not yet been defined ]: SET `B` = `false` INSTANTIATE `dependent_writer<false>` RETURN `false` ELSE: SET `B` = `true` INSTANTIATE `dependent_writer<true>` RETURN `true`
最后,概念的证明:
int main () {
constexpr int a = f ();
constexpr int b = f ();
static_assert (a != b, "fail");
}
constexpr
的Yes/No开关来指示一个类型是否被实例化。因此,您需要为每种类型都设置一个单独的开关。template<typename T>
struct inst_check_wrapper
{
friend constexpr int inst_flag(inst_check_wrapper<T>);
};
inst_check_wrapper<T>
本质上是用于包装一个适用于您所给定的任何类型的开关。它只是原始示例的通用版本。
template<typename T>
struct writer
{
friend constexpr int inst_flag(inst_check_wrapper<T>)
{
return 0;
}
};
template <typename T, bool B = noexcept(inst_flag(inst_check_wrapper<T>()))>
constexpr bool is_instantiated()
{
return B;
}
最后,类型“registers”本身被标记为已初始化。在我的情况下:
template <typename T>
struct MyStruct
{
template <typename T1 = T, int = sizeof(writer<MyStruct<T1>>)>
MyStruct()
{}
};
int main ()
{
static_assert(!is_instantiated<MyStruct<int>>(), "failure");
MyStruct<int> a;
static_assert(is_instantiated<MyStruct<int>>(), "failure");
}
constexpr
,这意味着inline
,那么为什么该实现会存在odr问题? - ABu不,无法对未实例化的类进行编译时检查。但是你可以在调试版本中建立一个(静态)实例化类的映射表,在运行时进行检查。
然而,通过比较预期实例化类列表与实际实例化类来分析链接的二进制文件应该是可能的(但这已经超出了编译时和我的知识范围)。
没有办法做到那样。所以我会说:不行。