为什么可变函数模板常量中的这个 constexpr 不是常量?
Why isn't this constexpr in a variadic function template constant?
在我的 class(这是一个可变 class 模板)中,我需要一个 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 年,我遇到了编译错误 "expression did not evaluate to a constant."
我不确定是什么不允许表达式保持不变。我尝试编译不同的东西以确保它们可以保持不变。我已经尝试在 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
?
"expression did not evaluate to a constant." 似乎不是根本原因。您的 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
。
你的代码的问题是,当你用一个 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 开始使用。
一种可能的(优雅的,恕我直言)解决方案,也适用于 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()
return 一个 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()
的ground case(零型版本)已经不用了,可以删掉
无论如何,正如 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;
}
在我的 class(这是一个可变 class 模板)中,我需要一个 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 年,我遇到了编译错误 "expression did not evaluate to a constant."
我不确定是什么不允许表达式保持不变。我尝试编译不同的东西以确保它们可以保持不变。我已经尝试在 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
?
"expression did not evaluate to a constant." 似乎不是根本原因。您的 static_max
和 maxSizeOf
需要修改以使编译器满意。您可以参考
例如:
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
。
你的代码的问题是,当你用一个 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 开始使用。
一种可能的(优雅的,恕我直言)解决方案,也适用于 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()
return 一个 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()
的ground case(零型版本)已经不用了,可以删掉
无论如何,正如 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;
}