是否可以检查在编译时是否已实例化模板类型,以便我可以在enable_if专业化中使用此信息?
假设我有
template <typename T> struct known_type { };
如果在编译时实例化了known_type,我可以以某种方式定义其值为true的is_known_type吗?
如果您利用特定表达式可能会或可能不会在constexpr
s中使用的事实,并且可以查询以查看所拥有的每个候选者的状态,则可以执行此操作。特别是在我们的情况下,constexpr
没有定义的s不能作为常量表达式传递,这是常量表达式noexcept
的保证。因此,noexcept(...)
返回true
信号表明存在正确定义的constexpr
。
本质上,这将constexpr
s视为Yes / No开关,并在编译时引入状态。
请注意,这几乎是一种技巧,您将需要针对特定编译器的变通办法(请参见前面的文章),并且此特定的friend
基于实现的实施可能会因将来的标准修订而被认为是错误的。
这样就不会了...
用户FilipRoséen在他的专门文章中提出了这个概念。
他的示例实现带有引述的解释:
constexpr int flag (int);
constexpr函数可以处于以下两种状态之一:它可以在常量表达式中使用,也可以不使用-如果它缺少定义,它会自动属于后一种类别-没有其他状态(除非我们考虑未定义的行为)。
通常,constexpr函数应该被正确地对待。函数,但我们也可以将它们视为具有类似于bool类型的“变量”的单独句柄,其中每个“变量”可以具有两个值之一;可用或不可用。
在我们的程序中,如果您认为flag只是那样,它会有所帮助;句柄(不是功能)。原因是我们永远不会真正在评估的上下文中调用flag,我们只对它的当前状态感兴趣。
template<class Tag>
struct writer {
friend constexpr int flag (Tag) {
return 0;
}
};
writer是一个类模板,在实例化时将在其周围的名称空间中创建一个函数定义(具有签名int标志(Tag),其中Tag是模板参数)。
如果我们再次将constexpr函数视为某些变量的句柄,则可以将writer的实例视为对朋友声明中该函数后面的变量可用的值的无条件写入。
template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };
如果您认为dependent_writer看起来像是一个毫无意义的间接调用,我不会感到惊讶。为什么不直接在我们要使用它的地方实例化writer,而不是通过dependent_writer?
- 作者的实例化必须依赖于某种因素来防止立即实例化,并且;
- dependent_writer用于可以将bool类型的值用作依赖项的上下文中。
template<
bool B = noexcept (flag (0)),
int = sizeof (dependent_writer<B>)
>
constexpr int f () {
return B;
}
上面的内容可能看起来有些怪异,但实际上非常简单。
- 如果flag(0)是一个常量表达式,则将B设置为true,否则将B设置为false。
- 隐式实例化dependent_writer(sizeof需要一个完全定义的类型)。
该行为可以用以下伪代码表示:
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
是/否”开关指示类型是否已实例化。因此,对于每种类型,您都需要一个单独的开关。
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;
}
最终,类型“注册”为已初始化。就我而言:
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");
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句