为什么可变参数函数模板中的这个 constexpr 不是常数?
Why isn't this constexpr in a variadic function template constant?
在我的类(这是一个可变类模板)中,我需要一个constexpr
作为可变模板中传递的最大类型的sizeof()
。像这样:
template<class... Types>
class DiscriminatedUnion
{
.
.
.
static constexpr auto value = maxSizeOf<Types...>();
我为maxSizeOf()
想出的代码如下:
template <class T>
static constexpr T static_max(T a, T b) {
return a < b ? b : a;
}
template <class T, class... Ts>
static constexpr T static_max(T a, Ts... bs) {
return static_max(a, static_max(bs...));
}
template <class T>
static constexpr int maxSizeOf() {
return sizeof(T);
};
template <class T, class... Ts>
static constexpr int maxSizeOf() {
return static_max(sizeof(T), maxSizeOf<Ts...>());
};
但在Visual Studio 2017中,我收到了一个编译错误"表达式未计算为常量"。
我不确定是什么不允许表达式保持不变。我试着编译不同的东西,以确保它们可以是恒定的。我曾尝试在constexpr
函数中使用带有模板参数的sizeof()
,这是有效的,因为在编译时类型的大小总是已知的,所以我希望这一点。整数运算和比较在constexpr
函数中似乎是有效的,但我再次尝试了一些以进行验证。然后,我尝试在变量模板方法中使用整数算术,不使用sizeof()
,并使用以下内容:
template <class T>
static constexpr int maxSizeOf(int n) {
return n;
};
template <class T, class... Ts>
static constexpr int maxSizeOf(int n) {
return static_max(n, maxSizeOf<Ts...>(n + 1));
};
static constexpr int numBytes = maxSizeOf<Types...>(1);
这是行不通的。所以我认为这一定是与可变方法模板扩展有关。但这应该能够成为编译时常数,因为可变模板包总是在编译时扩展的。有人知道为什么这些不能是constexpr
吗?
代码的问题是,当您使用单个T
类型调用max_sizeof<T>()
时,两个
template <class T>
static constexpr int maxSizeOf() {
return sizeof(T);
};
和
template <class T, class... Ts>
static constexpr int maxSizeOf() {
return static_max(sizeof(T), maxSizeOf<Ts...>());
};
匹配。因此编译器无法选择正确的。
正如dontpanic所建议的那样,您可以使用if constexpr ( sizeof...(Ts) )
进行求解,但if constexpr
只能从C++17开始使用。
一个可能的(也是优雅的,IMHO)解决方案,也适用于C++11和C++14,是删除唯一的一个类型函数,并添加以下零类型函数
template <int = 0>
static constexpr std::size_t maxSizeOf()
{ return 0u; };
这样,当您调用maxSizeOf<Ts...>()
时,当sizeof...(Ts) > 0u
时,会调用一个或多个类型版本;当sizeof...(Ts) == 0u
时(即:当Ts...
列表为empy时),int = 0
(无类型)匹配。
另一个建议:sizeof()
是std::size_t
值,因此如果maxSizeOf()
返回std::size_t
更好
以下是一个完整的工作(也是C++11)解决方案
#include <iostream>
template <typename T>
static constexpr T static_max (T a, T b)
{ return a < b ? b : a; }
template <typename T, typename ... Ts>
static constexpr T static_max (T a, Ts ... bs)
{ return static_max(a, static_max(bs...)); }
template <int = 0>
static constexpr std::size_t maxSizeOf()
{ return 0u; };
template <typename T, typename ... Ts>
static constexpr std::size_t maxSizeOf()
{ return static_max(sizeof(T), maxSizeOf<Ts...>()); };
template <typename ... Ts>
struct foo
{ static constexpr auto value = maxSizeOf<Ts...>(); };
int main ()
{
std::cout << foo<int, long, long long>::value << std::endl;
}
但是,正如aschepler所观察到的(谢谢!),这个解决方案有效,但根本不使用static_max()
的可变版本。
另一种使用static_max()
的可变版本的方法是重写maxSizeOf()
的可变版本,不是以递归的方式,而是简单地拆包可变列表,如下所示
template <typename ... Ts>
static constexpr std::size_t maxSizeOf()
{ return static_max(sizeof(Ts)...); }
现在是maxSizeOf()
的基本情况(零型版本),它不再使用,可以删除。
无论如何,正如NathanOliver所建议的,您可以使用std::max()
(接收初始值设定项列表的版本),从C++14开始,它就是constexpr
。
因此,从C++14开始,您可以简单地编写
#include <algorithm>
#include <iostream>
template <typename ... Ts>
struct foo
{ static constexpr auto value = std::max({sizeof(Ts)...}); };
int main ()
{
std::cout << foo<int, long, long long>::value << std::endl;
}
"表达式未计算为常量。"似乎不是根本原因。您的static_max
和maxSizeOf
需要进行修改以使编译器满意。你可以参考这篇文章,看看如何在不同的C++标准下做到这一点。
例如:
template <class T, class... Ts>
static constexpr T static_max(T a, Ts... bs) {
if constexpr (sizeof...(Ts) == 0)
return a;
else
return std::max(a, static_max(bs...));
}
template <class T, class... Ts>
static constexpr int maxSizeOf(int n) {
if constexpr (sizeof...(Ts) == 0)
return n;
else
return static_max(n, maxSizeOf<Ts...>(n + 1));
};
实际上,我们根本不需要static_max
。这里我们只需要在2个值内找到最大值,并且std::max
已经存在。
编辑:看来我们也不需要maxSizeOf
。。。正如Nathan在评论中提到的,std::max
也可以处理initializer_list
。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 多成员Constexpr结构初始化
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- Visual C++ constexpr Hints
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 为什么std::isnan 不是 constexpr?
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 更多constexpr容器是否需要mark_immutable_if_consexpr
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 表达式未评估为常数两个级别的constexpr函数(编译器错误?)
- 错误!Constexpr变量必须通过常数表达式constexpr初始化
- 我应该更喜欢在函数中的常数:constexpr const或enum
- 为什么可变参数函数模板中的这个 constexpr 不是常数?
- 当constexpr成员函数中可以使用非常数成员时
- C++:为什么这个 constexpr 不是编译时常数