c++11 - 为什么我的可变参数模板参数验证器拒绝在编译时进行评估?
问题描述
我有一个递归可变参数模板函数,可用于在编译时评估其参数,以确保它们都不大于指定的最大值:
#include <type_traits>
// base-case
template <int MaxVal, typename T>
constexpr T returnZeroIffValuesAreNotTooBig(const T t)
{
return (t>MaxVal)?1:0;
}
// recursive-case
template <int MaxVal, typename T, typename... Rest> constexpr T returnZeroIffValuesAreNotTooBig(const T t, Rest&&... rest)
{
return returnZeroIffValuesAreNotTooBig<MaxVal>(t)
+ returnZeroIffValuesAreNotTooBig<MaxVal>(std::forward<Rest>(rest)...);
}
int main(int argc, char ** argv)
{
static_assert(returnZeroIffValuesAreNotTooBig<6>(1,2,3)==0, "compiles (as expected)");
static_assert(returnZeroIffValuesAreNotTooBig<6>(1,2,3,4,5,6,7)==0, "generates compile-time error (as expected, because one of the args is greater than 6)");
return 0;
}
...到目前为止,一切都很好,以上所有对我来说都很好。
现在我想在我的类的可变参数构造函数中使用该函数:
template<int MaxVal> class MyClass
{
public:
template<typename ...Vals> constexpr explicit MyClass(Vals&&... vals)
{
// verify at compile-time that all values in (vals) are <= MaxVal
static_assert(returnZeroIffValuesAreNotTooBig<MaxVal>(std::forward<Vals>(vals)...)==0, "why doesn't this compile?");
}
};
int main(int argc, char ** argv)
{
MyClass<5> b(1,2,3,4); // should compile, but doesn't!?
return 0;
}
...这不会编译;相反,我总是收到此错误:
$ g++ -std=c++11 ./test.cpp
./test.cpp:12:80: error: static_assert expression is not an integral constant
expression
...returnZeroIffValuesAreNotTooBig<MaxVal>(std::forward<Vals>(vals)...)==0...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
./test.cpp:21:15: note: in instantiation of function template specialization
'MyClass<5>::MyClass<int, int, int, int>' requested here
MyClass<5> b(1,2,3,4);
^
1 error generated.
...显然我的完美转发是不完美的,这会阻止returnZeroIffValuesAreNotToBig
函数在编译时被评估?有人可以给我一个提示,让我知道我需要做些什么才能让它发挥作用吗?
解决方案
完美转发是无罪的。
正如 Caramiriel 所指出的,您不能在static_assert()
测试中使用函数参数。
我知道构造函数是constexpr
,但是constexpr
构造函数(像任何其他constexpr
函数一样)可以根据情况在编译时或运行时执行。
因此static_assert()
,具有可能仅在运行时已知的值的测试是错误的。
此外,在您的特定情况下
MyClass<5> b(1,2,3,4);
你没有这么定义b
编译constexpr
器,即使可以选择在编译时执行它,通常也是在运行时执行它。
如果要执行 a static_assert()
,则必须使这些值在编译时可用。我知道的方式是将值作为模板值传递。不幸的是,没有办法为构造函数显式模板值,所以我看到了两种可能的方法
1)传递vals...
作为类模板参数。
如下所示(注意:代码未经测试)
template<int MaxVal, int ... vals> class MyClass
{
public:
constexpr MyClass ()
{
// verify at compile-time that all values in (vals) are <= MaxVal
static_assert(returnZeroIffValuesAreNotTooBig<MaxVal>(vals...)==0,
"why doesn't this compile?");
}
};
MyClass<5, 1, 2, 3, 4> b;
在这种情况下,您也可以将 放在static_assert()
类的主体中,而不必放在构造函数的主体中。
2) 在构造函数参数中传递vals...
as 模板参数std::integer_sequence
(仅从 C++14 开始可用,但用 C++11 中的自定义类替换它很简单)。
如下所示(注意:代码未经测试)
template<int MaxVal> class MyClass
{
public:
template <int ... vals>
constexpr MyClass (std::integer_sequence<int, vals...> const &)
{
// verify at compile-time that all values in (vals) are <= MaxVal
static_assert(returnZeroIffValuesAreNotTooBig<MaxVal>(vals...)==0,
"why doesn't this compile?");
}
};
MyClass<5> b(std::integer_sequence<int, 1, 2, 3, 4>{});
推荐阅读
- f# - F# 将 CsvFile 转换为 Json 对象数组
- python - 使用 TFDebertaForSequenceClassification 进行二元分类时,错误形状并非不兼容
- regex - 如何调整这个 camelCase 正则表达式以允许首字母缩略词?
- spring-boot - @PreAuthorize 使验证不适用于原始类型
- c# - 在 icriteria 中选择常量/值
- c++ - ros msg::ptr 和 msg::constPtr 有什么区别?
- android - 需要但未调用:实际上,与此模拟的交互为零。共享偏好
- c# - EWS API 多流订阅混淆
- if-statement - 如何使用参数化的 sparql 查询插入数据?
- python-2.7 - 类似于交互式标记的两帧之间的旋转