我是 C++ 模板的新手,我正在尝试创建一个 constexpr 模板函数,该函数采用返回类型 bool (在本示例中为输入类型 int)的其他函数的参数包,并检查给定值是否满足所有这些参数。
我提取了重要的代码,并留下了一些不相关的实现。
下面的第 1 项是我希望函数的行为方式,其中 is_in_v 是一个元函数,用于检查给定类型的值是否包含在其后面的值束中。 这本身工作得很好,但当我尝试传递 func(b)... 时会抱怨,因为 b 不是常量表达式。 这对我来说很有意义,但是 Item 2 在编译时似乎表现良好,即使它传递的是相同的值 b。 这让我想知道我的想法是否可以实现,因为对我来说,感觉第 2 项中的工作本质上与我想做的事情并没有那么远,但我不知道如何解决这个错误.
constexpr bool f1 (int a) { return true; }
constexpr bool f2 (int a) { return false; }
constexpr bool f3 (int a) { return a > 3; }
//Item 1
template<bool (*...func)(int)>
constexpr bool test(int b) {
return !is_in_v<bool, false, func(b)...>;
}
//Item 2
template<bool (*func)(int)>
constexpr bool test2(int b) {
return func(b);
}
...
int main () {
static_assert(test<f1, f3>(5)); //ERROR: b is not a constant expression
static_assert(test2<f3>(5)); //works fine
}
如果有人能告诉我我正在尝试做的事情是否有意义或者是否有其他解决方案,我将不胜感激。我尝试过通过 const 引用传递,这对函数 f1 和 f2 有效,但仅将测试中的参数 b 不是 constexpr 的问题委托给 f3 中的参数 a。
!is_in_v<bool, false, func(b)...>;
这似乎是编写可变参数和表达式的一种相当迂回的方式。 C++17 支持折叠表达式,所以它确实应该写得更简单为
template<bool (*...func)(int)>
constexpr bool test(int b) {
return (func(b) && ...);
}
关于模板的事情是,它们的非类型参数必须始终且无条件地是常量表达式。函数参数,即使在 constexpr
函数中,也永远不会被认为可在常量表达式中使用,因此不能用于形成常量表达式。它使
func(b)
作为模板参数无效。粗略地说,
test2
之所以有效,是因为它不假设
b
可在常量表达式中使用。它只是简单地列出了一个计算,当遵循具有编译时常量的抽象机的规则时,编译器可以执行该计算。简而言之,这就是持续评估。当您将函数标记为
constexpr
时,在某些条件下,编译器可能会使用它来计算常量。但这并不意味着函数参数是恒定的。事实上,不可能是这个意思。无需深入研究该规范的细节,而是要解释其原因,请通过矛盾来考虑它是否“确实”。假设你可以这样写:
constexpr auto foo(int a) {
return std::integral_constant<int, a>{}; // Make believe it is valid to use a.
}
然后我们发现
foo(1)
和
foo(2)
返回不同的类型!这不是 C++ 设计的工作方式。函数签名从不依赖于其参数获得的值!而且我什至没有触及
constexpr
函数在运行时存在的事实,因为它们也是常规函数,那么签名难题就变得更大了。