我很难理解概念和约束是如何工作的。到目前为止,我总是设法避免使用类型traits和static_assert
或std::enable_if
(甚至SFINAE),但我想与c++20协调(至少在这一部分,因为我对几乎所有与c++20一起添加的东西都有同样的理解困难)。
我有一个带有可变参数模板参数的函数,我想只接受高于阈值的整数值,比如2
。
为此,我定义了一个integral
概念,然后添加了一个requires
子句来添加阈值约束,这给了我:
template <typename T>
concept integral = std::is_integral<T>::value;
template <integral ... Ts>
void f(Ts ... ts) requires (... && (ts > 2))
{
//blablabla
}
这个编译得很好。但是当我尝试使用argumentsn调用f()
(例如f(8, 6);
)时,我总是得到编译时错误(GCC):error: 'ts#0' is not a constant expression
完整错误跟踪(GCC):
<source>: In substitution of 'template<class ... Ts> requires (... && integral<Ts>) void f(Ts >...) requires (... && ts > 2) [with Ts = {int, int}]':
<source>:15:6: required from here
<source>:8:6: required by the constraints of 'template<class ... Ts> requires (... && integral<Ts>) void f(Ts ...) requires (... && ts > 2)'
<source>:8:43: error: 'ts#0' is not a constant expression
8 | void f(Ts ... ts) requires (... && (ts > 2))
| ~~~~~~~~~~~~~~~^~
<source>: In function 'int main()':
<source>:15:6: error: no matching function for call to 'f(int, int)'
15 | f(8, 6);
| ~^~~~~~
<source>:8:6: note: candidate: 'template<class ... Ts> requires (... && integral<Ts>) void f(Ts >...) requires (... && f::ts > 2)'
8 | void f(Ts ... ts) requires (... && (ts > 2))
| ^
<source>:8:6: note: substitution of deduced template arguments resulted in errors seen above
我不明白的是为什么参数必须是常量表达式,为什么8
不被认为是常量表达式?
1条答案
按热度按时间41zrol4v1#
函数参数的值不能在函数约束中使用。重现问题的一个更简单的方法是:
这会产生错误:
x
的值在编译时是未知的,函数约束只能验证编译时属性。即使我们使用x = 0
调用f
,f
也需要使用 * 所有 * 可能的参数,而不仅仅是0
。如果你想要一个只能保存大于2的值的类型,你可以这样做:
关于
[[assume(v > 2)]]
的说明我们使用
[[assume]]
(C++23起)来启用编译器优化,因为greater_two_integer
总是包含一个值> 2
。该属性应用于转换运算符中的空语句,因此当我们从对象中提取值时,如果v <= 2
,则为未定义行为。这是安全的,因为构造函数包含
assert(v > 2)
,这意味着:v > 2
v > 2
std::memcpy
写入类中或通过reinterpret_cast<int*>
写入其值来打破这个类不变量。然而,这两种方法显然都是搬起石头砸自己的脚,它们不是偶然发生的。