問題描述
是否可以在編譯時(shí)檢查模板類型是否已實(shí)例化,以便我可以在 enable_if 專業(yè)化中使用此信息?
假設(shè)我有
template 結(jié)構(gòu)已知類型{};
如果 known_type 在編譯時(shí)實(shí)例化,我可以以某種方式定義一些值為 true 的 is_known_type 嗎?
如果您利用特定表達(dá)式可能會或可能不會在需要 constexpr
的地方使用這一事實(shí),則可以這樣做,并且您可以查詢以查看您擁有的每個(gè)候選人的狀態(tài).特別是在我們的例子中,沒有定義的 constexpr
不能作為常量表達(dá)式傳遞,而 noexcept
是常量表達(dá)式的保證.因此,noexcept(...)
返回 true
表示存在正確定義的 constexpr
.
本質(zhì)上,這將 constexpr
s 視為 Yes/No 開關(guān),并在編譯時(shí)引入狀態(tài).
請注意,這幾乎是一個(gè) hack,您將需要針對特定??編譯器的變通方法(請參閱前面的文章),并且此特定的基于 friend
的實(shí)現(xiàn)可能會被未來的修訂版視為格式錯(cuò)誤標(biāo)準(zhǔn).
除此之外...
用戶 Filip Roséen 在 他的文章專門針對它.
他的示例實(shí)現(xiàn)是,帶有引用的解釋:
constexpr int flag (int);
<塊引用>
constexpr 函數(shù)可以處于兩種狀態(tài)之一;要么是可用于常量表達(dá)式,或者不是 - 如果它缺少定義它自動(dòng)屬于后一類 - 沒有其他狀態(tài)(除非我們考慮未定義的行為).
通常,constexpr 函數(shù)應(yīng)該完全按照它們的方式來對待.是;函數(shù),但我們也可以將它們視為單獨(dú)的句柄具有類似于 bool 類型的變量",其中每個(gè)變量"都可以具有兩個(gè)值之一;可用或不可用.
在我們的程序中,如果您認(rèn)為 flag 就是這樣,它會有所幫助;一個(gè)手柄(不是函數(shù)).原因是我們永遠(yuǎn)不會真正調(diào)用 flag在評估上下文中,我們只對其當(dāng)前狀態(tài)感興趣.
template結(jié)構(gòu)作者{朋友 constexpr int 標(biāo)志(標(biāo)簽){返回0;}};
<塊引用>
writer 是一個(gè)類模板,它在實(shí)例化時(shí)會創(chuàng)建一個(gè)函數(shù)在其周圍命名空間中的定義(具有簽名 int 標(biāo)志(Tag),其中 Tag 是模板參數(shù)).
如果我們再次將 constexpr 函數(shù)視為某些變量,我們可以將 writer 的實(shí)例化視為無條件地將可用值寫入后面的變量朋友聲明中的函數(shù).
templatestructdependent_writer : writer{ };
<塊引用>
如果你認(rèn)為dependent_writer 看起來像一個(gè)相當(dāng)無意義的間接;為什么不直接實(shí)例化writer我們想在哪里使用它,而不是通過dependent_writer?
- writer 的實(shí)例化必須依賴某些東西來防止立即實(shí)例化,并且;
- dependent_writer 用于可以將 bool 類型的值用作依賴項(xiàng)的上下文.
模板)>constexpr int f() {返回 B;}
<塊引用>
上面可能看起來有點(diǎn)奇怪,但其實(shí)很簡單;
- 如果 flag(0) 是一個(gè)常量表達(dá)式,將設(shè)置 B = true,否則 B = false,并且;
- 隱式實(shí)例化dependent_writer(sizeof 需要完全定義的類型).
行為可以用以下偽代碼表示:
IF [ `int flag (int)` 尚未定義 ]:SET `B` = `false`實(shí)例化`dependent_writer`返回`假`別的:SET `B` = `true`實(shí)例化`dependent_writer`返回`真`
最后是概念證明:
int main() {constexpr int a = f();constexpr int b = f();static_assert (a != b, "fail");}
<小時(shí)>
我將此應(yīng)用于您的特定問題.這個(gè)想法是使用 constexpr
Yes/No 開關(guān)來指示一個(gè)類型是否已經(jīng)被實(shí)例化.因此,您需要為您擁有的每種類型設(shè)置一個(gè)單獨(dú)的開關(guān).
templatestruct inst_check_wrapper{朋友 constexpr int inst_flag(inst_check_wrapper<T>);};
inst_check_wrapper<T>
本質(zhì)上為您提供的任何類型包裝了一個(gè)開關(guān).這只是原始示例的通用版本.
template結(jié)構(gòu)作者{朋友 constexpr int inst_flag(inst_check_wrapper<T>){返回0;}};
開關(guān)切換器與原始示例中的切換器相同.它提出了您使用的某種類型的開關(guān)的定義.為了便于檢查,添加一個(gè)輔助開關(guān)檢查器:
template ()))>constexpr bool is_instantiated(){返回 B;}
最后,類型注冊"為初始化.就我而言:
template 結(jié)構(gòu)體{模板 >)>我的結(jié)構(gòu)(){}};
只要請求特定的構(gòu)造函數(shù),開關(guān)就會打開.示例:
int main(){static_assert(!is_instantiated>(), "failure");MyStruct一個(gè);static_assert(is_instantiated>(), "failure");}
在 Coliru 上直播.
Is it possible to check if a template type has been instantiated at compile time so that I can use this information in an enable_if specialization?
Let's say I have
template <typename T> struct known_type { };
Can I somehow define some is_known_type whose value is true if known_type is instantiated at compile time?
It's possible to do this if you leverage the fact that specific expressions may or may not be used in places where constexpr
s are expected, and that you can query to see what the state is for each candidate you have. Specifically in our case, the fact that constexpr
s with no definition cannot pass as constant expressions and noexcept
is a guarantee of constant expressions. Hence, noexcept(...)
returning true
signals the presence of a properly defined constexpr
.
Essentially, this treats constexpr
s as Yes/No switches, and introduces state at compile-time.
Note that this is pretty much a hack, you will need workarounds for specific compilers (see the articles ahead) and this specific friend
-based implementation might be considered ill-formed by future revisions of the standard.
With that out of the way...
User Filip Roséen presents this concept in his article dedicated specifically to it.
His example implementation is, with quoted explanations:
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`
Finally, the proof of concept:
int main () {
constexpr int a = f ();
constexpr int b = f ();
static_assert (a != b, "fail");
}
I applied this to your particular problem. The idea is to use the constexpr
Yes/No switches to indicate whether a type has been instantiated. So, you'll need a separate switch for every type you have.
template<typename T>
struct inst_check_wrapper
{
friend constexpr int inst_flag(inst_check_wrapper<T>);
};
inst_check_wrapper<T>
essentially wraps a switch for whatever type you may give it. It's just a generic version of the original example.
template<typename T>
struct writer
{
friend constexpr int inst_flag(inst_check_wrapper<T>)
{
return 0;
}
};
The switch toggler is identical to the one in the original example. It comes up with the definition for the switch of some type that you use. To allow for easy checking, add a helper switch inspector:
template <typename T, bool B = noexcept(inst_flag(inst_check_wrapper<T>()))>
constexpr bool is_instantiated()
{
return B;
}
Finally, the type "registers" itself as initialized. In my case:
template <typename T>
struct MyStruct
{
template <typename T1 = T, int = sizeof(writer<MyStruct<T1>>)>
MyStruct()
{}
};
The switch is turned on as soon as that particular constructor is asked for. Sample:
int main ()
{
static_assert(!is_instantiated<MyStruct<int>>(), "failure");
MyStruct<int> a;
static_assert(is_instantiated<MyStruct<int>>(), "failure");
}
Live on Coliru.
這篇關(guān)于編譯時(shí)模板實(shí)例化檢查的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!