编译时模板实例化检查
Compile time template instantiation check
是否可以在编译时检查模板类型是否已实例化,以便我可以在 enable_if 专业化中使用此信息?
假设我有
template <typename T> struct known_type { };
如果 known_type 在编译时被实例化,我能以某种方式定义一些值为 true 的 is_known_type 吗?
没有办法做到这一点。所以我会说:不。
不,无法对未实例化的 类 进行编译时检查。但是,您可以建立实例化 类(在调试版本中)的(静态)映射,您可以在 运行 时间检查它。
但是,通过比较预期实例化 类 与实际实例化 类 的列表来分析链接的二进制文件应该是可能的(但那是过去的编译时间和我所不知道的)。
如果您利用特定表达式可能会或可能不会在预期 constexpr
的地方使用这一事实,并且您可以查询以查看每个候选人的状态,则可以执行此操作你有。具体来说,在我们的例子中,没有定义的 constexpr
不能作为常量表达式传递,而 noexcept
是常量表达式的保证。因此,noexcept(...)
返回 true
表示存在正确定义的 constexpr
.
本质上,这将 constexpr
s 视为 Yes/No 开关,并在编译时引入状态。
请注意,这几乎是一个 hack,您将需要针对特定编译器的解决方法(请参阅前面的文章),并且这个基于 friend
的特定实现可能会被标准的未来修订视为格式错误.
除此之外...
用户 Filip Roséen presents this concept in his article 专门致力于此。
他的示例实现是,引用了解释:
constexpr int flag (int);
A constexpr function can be in either one of two states; either it is
usable in a constant-expression, or it isn't - if it lacks a
definition it automatically falls in the latter category - there is no
other state (unless we consider undefined behavior).
Normally, constexpr functions should be treated exactly as what they
are; functions, but we can also think of them as individual handles to
"variables" having a type similar to bool, where each "variable" can
have one of two values; usable or not-usable.
In our program it helps if you consider flag to be just that; a handle
(not a function). The reason is that we will never actually call flag
in an evaluated context, we are only interested in its current state.
template<class Tag>
struct writer {
friend constexpr int flag (Tag) {
return 0;
}
};
writer is a class template which, upon instantiation, will create a
definition for a function in its surrounding namespace (having the
signature int flag (Tag), where Tag is a template-parameter).
If we, once again, think of constexpr functions as handles to some
variable, we can treat an instantiation of writer as an
unconditional write of the value usable to the variable behind the
function in the friend-declaration.
template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };
I would not be surprised if you think dependent_writer looks like a
rather pointless indirection; why not directly instantiate writer
where we want to use it, instead of going through dependent_writer?
- Instantiation of writer must depend on something to prevent immediate instantiation, and;
- dependent_writer is used in a context where a value of type bool can be used as dependency.
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");
}
是否可以在编译时检查模板类型是否已实例化,以便我可以在 enable_if 专业化中使用此信息?
假设我有
template <typename T> struct known_type { };
如果 known_type 在编译时被实例化,我能以某种方式定义一些值为 true 的 is_known_type 吗?
没有办法做到这一点。所以我会说:不。
不,无法对未实例化的 类 进行编译时检查。但是,您可以建立实例化 类(在调试版本中)的(静态)映射,您可以在 运行 时间检查它。
但是,通过比较预期实例化 类 与实际实例化 类 的列表来分析链接的二进制文件应该是可能的(但那是过去的编译时间和我所不知道的)。
如果您利用特定表达式可能会或可能不会在预期 constexpr
的地方使用这一事实,并且您可以查询以查看每个候选人的状态,则可以执行此操作你有。具体来说,在我们的例子中,没有定义的 constexpr
不能作为常量表达式传递,而 noexcept
是常量表达式的保证。因此,noexcept(...)
返回 true
表示存在正确定义的 constexpr
.
本质上,这将 constexpr
s 视为 Yes/No 开关,并在编译时引入状态。
请注意,这几乎是一个 hack,您将需要针对特定编译器的解决方法(请参阅前面的文章),并且这个基于 friend
的特定实现可能会被标准的未来修订视为格式错误.
除此之外...
用户 Filip Roséen presents this concept in his article 专门致力于此。
他的示例实现是,引用了解释:
constexpr int flag (int);
A constexpr function can be in either one of two states; either it is usable in a constant-expression, or it isn't - if it lacks a definition it automatically falls in the latter category - there is no other state (unless we consider undefined behavior).
Normally, constexpr functions should be treated exactly as what they are; functions, but we can also think of them as individual handles to "variables" having a type similar to bool, where each "variable" can have one of two values; usable or not-usable.
In our program it helps if you consider flag to be just that; a handle (not a function). The reason is that we will never actually call flag in an evaluated context, we are only interested in its current state.
template<class Tag>
struct writer {
friend constexpr int flag (Tag) {
return 0;
}
};
writer is a class template which, upon instantiation, will create a definition for a function in its surrounding namespace (having the signature int flag (Tag), where Tag is a template-parameter).
If we, once again, think of constexpr functions as handles to some variable, we can treat an instantiation of writer as an unconditional write of the value usable to the variable behind the function in the friend-declaration.
template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };
I would not be surprised if you think dependent_writer looks like a rather pointless indirection; why not directly instantiate writer where we want to use it, instead of going through dependent_writer?
- Instantiation of writer must depend on something to prevent immediate instantiation, and;
- dependent_writer is used in a context where a value of type bool can be used as dependency.
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");
}