在我的班级(这是一个可变参数类模板)中,对于可变参数模板中传递的最大类型的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中,我收到一个编译错误“表达式未求值为常数”。
我不确定是什么不允许表达式保持不变。我尝试编译不同的东西以确保它们可以保持不变。我尝试在sizeof()
函数中使用带有模板参数的constexpr
,该函数可以正常工作,因为在编译时总是知道类型的大小,所以我希望这样做。整数算术和比较似乎在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
?
答案 0 :(得分:2)
代码的问题是,当您使用单个max_sizeof<T>()
类型调用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...>());
};
个匹配项。因此,编译器无法选择正确的版本。
您可以按照{tnpanic的建议,使用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...
列表为空)时,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;
}
答案 1 :(得分:1)
“表达式的计算结果不为常数。”似乎不是根本原因。您的static_max
和maxSizeOf
需要进行修改以使编译器满意。您可以参考this post来了解如何在不同的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
...就像内森在评论中提到的那样,std::max
也可以处理initializer_list
。