假设我们要创建一个模板类,该模板类只能用数字实例化,否则不应该编译。我的尝试:
#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
}
Live demo-所有三个主要的编译器似乎都能按预期工作。我知道,已经有一个类似的question,其答案引用了该标准。接受的答案使用temp.res.8和temp.dep.1来回答该问题。我认为我的问题有所不同,因为我只是在问我的示例,我不确定标准对此的看法。
我认为我的程序不是格式错误的,并且仅当编译器尝试实例化基本模板时,它才应该无法编译。 我的推理:
[温度深度1]:
在模板内部,某些构造的语义可能因一个实例而异。这样的构造取决于模板参数。
这应该使std::is_same<C,T>::value
依赖于T
。
[temp.res.8.1]:
如果模板中的语句或模板未实例化,则无法为模板或constexpr的子语句生成有效的专业化,或者
因为存在有效的专业化,所以不适用,特别是OnlyNumbers<C>
是有效的,可以在类内部使用,例如定义成员指针变量(ptr
)。实际上,通过删除断言并取消注释ptr
,OnlyNumbers<Foo>
行,即可编译代码。
[温度解析度8.2-8.4]不适用。
我的问题是:我的推理正确吗?仅当实例化模板时,才能使用static_assert
编译特定[class] *模板的安全,标准方法吗?
*主要是我对类模板感兴趣,可以随时包含函数模板。但是我认为规则是相同的。
**这意味着没有T
可以用来从外部实例化模板,就像T=C
可以从内部使用一样。即使可以以某种方式访问C
,我也不认为有一种引用它的方法,因为它会导致这种递归OnlyNumbers<OnlyNumbers<...>::C>
。
编辑:
请澄清一下,我知道我可以构造一个表达式,如果其他任何专业都不匹配,则该表达式将完全为假。但这很快就会变得冗长,并且如果专业领域发生变化,很容易出错。
答案 0 :(得分:2)
静态断言可以直接在类中使用,而无需做任何复杂的事情。
#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实例化。由于静态断言无论如何都会失败,因此不会产生负面影响。
答案 1 :(得分:1)
嗯...我不明白你对
的含义[[temp.res.8.1]]不适用,因为存在有效的专业化,尤其是OnlyNumbers有效,并且可以在类内使用,例如定义一个成员指针变量(ptr)。
您能举例说明OnlyNumers
有效并基于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>>>
{ };
答案 2 :(得分:1)
由于主模板无法实例化,因此您的代码格式错误。请参阅Barry对您链接到的相关问题的答案中的标准报价。您用来确保不能满足明确规定的标准要求 的回旋方式无济于事。停止对抗您的编译器rsp。标准,并采用Handy999的方法。如果您仍然不想这样做,例如由于DRY的原因,达到目标的一种一致方法是:
{
data:
"bank",
render: function(data, type, row){
var selectBank =
'<select class="custom-select form-control renderBank" id="" name="billNotes['+index+'].billingNote.bank.id" data-url="/api/finance/master/banks" data-selected="'+data.id+'">';
renderSelect2Bank(data.id);
return selectBank;
}
},
function renderSelect2Bank(selectedBank){
$('select.renderBank').each(function (index) {
var that = this;
$(that).select2({
width: '100%',
placeholder: "Silah pilih",
language: "id"
});
$.ajax({
url: $(that).data('url'),
type: 'GET',
success: function(data){
if(data && data !== ""){
$.each(data, function (i, val){
if(selectedBank === val.id){
$(that).append(initAutofill(val.text, val.id, true));
}else{
$(that).append(initAutofill(val.text, val.id, false));
}
});
}
},
error: function(xhr, status, error){
errorCallback(xhr);
}
});
});
两句话:
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
以击败静态断言。我要说的就是他们的问题。请记住,每当您做出某种白痴证明时,自然就会使白痴变得更好。 ;-)认真地说, do 使API易于使用且不易滥用,但是您无法修复精神错乱,也不必费心尝试。TL; DR:KISS和SWYM(说出您的意思)