![](/img/trans.png)
[英]Is there a standard-compliant way to determine the alignment of a non-static member?
[英]A safe, standard-compliant way to make a class template specialization fail to compile using `static_assert` only if it is instantiated?
假設我們要創建一個只能用數字實例化的模板類,否則就不應該編譯。 我的嘗試:
#include <type_traits>
template<typename T, typename = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(std::is_same<C,T>::value, "T is not arithmetic type.");
//OnlyNumbers<C>* ptr;
};
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>{};
struct Foo{};
int main()
{
OnlyNumbers<int>{}; //Compiles
//OnlyNumbers<Foo>{}; //Error
}
現場演示 -所有三個主要的編譯器似乎都能按預期工作。 我知道已經有一個類似的問題 ,答案引用了該標准。 接受的答案使用temp.res.8和temp.dep.1來回答該問題。 我認為我的問題有所不同,因為我只是在問我的示例,我不確定標准對此的看法。
我認為我的程序不是格式錯誤的,並且僅當編譯器嘗試實例化基本模板時,它才應該無法編譯。 我的推理:
[temp.dep.1]:
在模板內部,某些構造的語義可能因一個實例而異。 這樣的構造取決於模板參數。
這應該使std::is_same<C,T>::value
取決於T
[temp.res.8.1]:
如果模板中的語句或模板未實例化,則無法為模板或constexpr的子語句生成有效的專業化名稱;或者
因為存在有效的專業化,所以不適用,特別是OnlyNumbers<C>
是有效的,並且可以在類內部用於例如定義成員指針變量( ptr
)。 實際上,通過刪除斷言並取消對ptr
注釋, OnlyNumbers<Foo>
行會編譯代碼。
[temp.res.8.2-8.4]不適用。
我的問題是:我的推理正確嗎? 僅在實例化模板的情況下,使用static_assert
編譯特定[class] *模板的方法是否安全,符合標准?
*基本上,我對類模板感興趣,請隨時包含函數模板。 但是我認為規則是相同的。
**這意味着沒有T
可以用於從外部實例化模板,就像可以從內部使用T=C
一樣。 即使可以某種方式訪問C
,我也不認為有一種引用它的方法,因為它導致了這種遞歸OnlyNumbers<OnlyNumbers<...>::C>
。
編輯:
為了明確起見,我知道我可以構造一個表達式,如果其他任何一個專業都不匹配,則該表達式將完全為假。 但這很快就會變得冗長,並且如果專業變更,則容易出錯。
靜態斷言可以直接在類中使用,而無需做任何復雜的事情。
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
// ....
};
在某些情況下,您可能會收到其他錯誤消息,因為對於非算術類型實例化OnlyNumbers可能會導致更多編譯錯誤。
我不時使用的一個技巧是
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
using TT = std::conditional_t<std::is_arithmetic_v<T>,T,int>;
// ....
};
在這種情況下,您的類將使用有效類型int實例化。 由於靜態斷言無論如何都會失敗,因此不會產生負面影響。
好吧...我不明白你的意思
[[temp.res.8.1]]不存在,因為存在有效的專業化,尤其是OnlyNumbers有效,並且可以在類內部用於例如定義成員指針變量(ptr)。
您能否舉一個OnlyNumers
有效和基於OnlyNumbers<C>
編譯主模板的OnlyNumbers<C>
?
無論如何,在我看來,重點就在於此。
如果你問
僅在實例化模板的情況下,使用static_assert編譯特定[class] *模板的方法是否安全,符合標准?
在我看來(可能不包括僅在另一個專業匹配的情況下才是真的測試)由於[temp.res.8.1],答案為“否”。
也許您可以打開一扇打開的門來允許實例化,但只有有人(真的!)想要實例化它時才可用。
例如,您可以添加第三個模板參數,其默認值不同,如下所示
template<typename T, typename U = void, typename V = int>
struct OnlyNumbers
{
static_assert(std::is_same<T, U>::value, "test 1");
static_assert(std::is_same<T, V>::value, "test 2");
};
通過這種方式,您可以打開合法實例化的門
OnlyNumbers<Foo, Foo, Foo> o1;
OnlyNumbers<void, void, void> o2;
OnlyNumbers<int, int> o3;
但僅說明至少第二種模板類型。
無論如何,為什么不簡單地避免定義模板的主版本呢?
// declared but (main version) not defined
template<typename T, typename = void>
struct OnlyNumbers;
// only specialization defined
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{ };
由於無法實例化主模板,因此您的代碼格式錯誤。 請參閱Barry對您鏈接到的相關問題的答案中的標准報價。 您用來確保不能滿足明確規定的標准要求的回旋方式無濟於事。 停止對抗您的編譯器rsp。 標准,並采用Handy999的方法。 如果您仍然不想這樣做,例如出於DRY的原因,那么達到目標的一種一致方法將是:
template<typename T, typename Dummy = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(! std::is_same<Dummy, void>::value, "T is not a number type.");
兩句話:
! std::is_arithmetic<T>::value
! std::is_arithmetic<T>::value
。 如果您對“數字”類型有多個重載,其中有些可以滿足標准的算術類型要求,而另一些可能不滿足(例如,來自多精度庫的類型),那么我概述的方法可能有意義。 OnlyNumbers<std::string, int>
來擊敗靜態斷言。 我要說的就是他們的問題。 請記住,每當您做出某種白痴證明時,自然就會使白痴變得更好。 ;-)說真的, 做化妝的API,易於使用,而難以被濫用,但你不能修復的瘋狂,不應該打擾嘗試。 TL; DR:KISS和SWYM(說出您的意思)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.