使用条件运算符递归计算模板值或函数时出现错误 C1202(堆栈溢出)
Error C1202 (stack overflow) when recursively computing a templated value or function when using a conditional operator
我正在实现的功能可以将游戏板单元格的坐标转换为该单元格的编号。
这就是我正在努力(但失败了)的工作。
#include <cstdint>
#include <utility>
using UInt32 = std::uint32_t;
template<UInt32... s>
using IndexSequence = std::integer_sequence<UInt32, s...>;
static constexpr UInt32 W = 8;
static constexpr UInt32 H = 8;
template<UInt32 x1, UInt32 x, UInt32 x2, UInt32 y1, UInt32 y2, UInt32... s>
static constexpr auto RegonImpl =
(y1 <= y2)
? (x <= x2)
? RegonImpl<x1, x + 1, x2, y1, y2, s..., W * y1 + x>
: RegonImpl<x1, x1, x2, y1 + 1, y2, s...>
: IndexSequence<s...>{};
template<UInt32 x1, UInt32 x2, UInt32 y1, UInt32 y2>
static constexpr auto Region = RegonImpl<x1, x1, x2, y1, y2>;
int main() {
constexpr auto idx = Region<0, 0, 5, 5>();
}
编译时出现错误C1202(递归类型或函数依赖上下文太复杂)。
错误输出:
... Indexes<8,8>::Region<0,0,1,7>(void) noexcept' being compiled
... Indexes<8,8>::RegionImpl<0,0,0,1,7>' being compiled
... Indexes<8,8>::RegionImpl<1,0,0,1,7,0>' being compiled
... Indexes<8,8>::RegionImpl<2,0,0,1,7,0,1>' being compiled
... Indexes<8,8>::RegionImpl<3,0,0,1,7,0,1,2>' being compiled
... Indexes<8,8>::RegionImpl<4,0,0,1,7,0,1,2,3>' being compiled
... Indexes<8,8>::RegionImpl<5,0,0,1,7,0,1,2,3,4>' being compiled
... Indexes<8,8>::RegionImpl<6,0,0,1,7,0,1,2,3,4,5>' being compiled
... Indexes<8,8>::RegionImpl<7,0,0,1,7,0,1,2,3,4,5,6>' being compiled
... Indexes<8,8>::RegionImpl<8,0,0,1,7,0,1,2,3,4,5,6,7>' being compiled
... Indexes<8,8>::RegionImpl<9,0,0,1,7,0,1,2,3,4,5,6,7,8>' being compiled
...
如您所见,条件 x <= x2
始终为真,但不应该为真。
我尝试按如下方式实现此功能:
template<UInt32... s, UInt32... t>
constexpr auto concat(IndexSequence<s...>, IndexSequence<t...>) noexcept {
return IndexSequence<s..., t...>{};
}
template<UInt32 x, UInt32 x1, UInt32 y1, UInt32 x2, UInt32 y2>
static constexpr auto RegionImpl() noexcept {
if constexpr (y1 <= y2) {
if constexpr (x <= x2) {
return concat(IndexSequence<W * y1 + x>{}, RegionImpl<x + 1, x1, y1, x2, y2>());
} else {
return RegionImpl<x1, x1, y1 + 1, x2, y2>();
}
} else {
return IndexSequence<>{};
}
}
template<UInt32 x1, UInt32 y1, UInt32 x2, UInt32 y2>
static constexpr auto Region() noexcept {
return RegionImpl<x1, x1, y1, x2, y2>();
}
有效。但是,如果使用 conditional operator (a ? b : c)
而不是 if statement
,则会发生相同的错误。
使用 conditional operator
时实际发生了什么?
这种情况下的三元条件不等同于if
语句,因为它是一个constexpr if
语句。
用constexpr if
语句,
If a constexpr if
statement appears inside a templated entity, and if condition is not value-dependent after instantiation, the discarded statement is not instantiated when the enclosing template is instantiated .
但是对于三元条件,模板总是被实例化。这导致无限递归。
请注意,如果将 constexpr if
替换为正常的 if
,则会出现相同的错误。
参见 DEMO。
我正在实现的功能可以将游戏板单元格的坐标转换为该单元格的编号。
这就是我正在努力(但失败了)的工作。
#include <cstdint>
#include <utility>
using UInt32 = std::uint32_t;
template<UInt32... s>
using IndexSequence = std::integer_sequence<UInt32, s...>;
static constexpr UInt32 W = 8;
static constexpr UInt32 H = 8;
template<UInt32 x1, UInt32 x, UInt32 x2, UInt32 y1, UInt32 y2, UInt32... s>
static constexpr auto RegonImpl =
(y1 <= y2)
? (x <= x2)
? RegonImpl<x1, x + 1, x2, y1, y2, s..., W * y1 + x>
: RegonImpl<x1, x1, x2, y1 + 1, y2, s...>
: IndexSequence<s...>{};
template<UInt32 x1, UInt32 x2, UInt32 y1, UInt32 y2>
static constexpr auto Region = RegonImpl<x1, x1, x2, y1, y2>;
int main() {
constexpr auto idx = Region<0, 0, 5, 5>();
}
编译时出现错误C1202(递归类型或函数依赖上下文太复杂)。
错误输出:
... Indexes<8,8>::Region<0,0,1,7>(void) noexcept' being compiled
... Indexes<8,8>::RegionImpl<0,0,0,1,7>' being compiled
... Indexes<8,8>::RegionImpl<1,0,0,1,7,0>' being compiled
... Indexes<8,8>::RegionImpl<2,0,0,1,7,0,1>' being compiled
... Indexes<8,8>::RegionImpl<3,0,0,1,7,0,1,2>' being compiled
... Indexes<8,8>::RegionImpl<4,0,0,1,7,0,1,2,3>' being compiled
... Indexes<8,8>::RegionImpl<5,0,0,1,7,0,1,2,3,4>' being compiled
... Indexes<8,8>::RegionImpl<6,0,0,1,7,0,1,2,3,4,5>' being compiled
... Indexes<8,8>::RegionImpl<7,0,0,1,7,0,1,2,3,4,5,6>' being compiled
... Indexes<8,8>::RegionImpl<8,0,0,1,7,0,1,2,3,4,5,6,7>' being compiled
... Indexes<8,8>::RegionImpl<9,0,0,1,7,0,1,2,3,4,5,6,7,8>' being compiled
...
如您所见,条件 x <= x2
始终为真,但不应该为真。
我尝试按如下方式实现此功能:
template<UInt32... s, UInt32... t>
constexpr auto concat(IndexSequence<s...>, IndexSequence<t...>) noexcept {
return IndexSequence<s..., t...>{};
}
template<UInt32 x, UInt32 x1, UInt32 y1, UInt32 x2, UInt32 y2>
static constexpr auto RegionImpl() noexcept {
if constexpr (y1 <= y2) {
if constexpr (x <= x2) {
return concat(IndexSequence<W * y1 + x>{}, RegionImpl<x + 1, x1, y1, x2, y2>());
} else {
return RegionImpl<x1, x1, y1 + 1, x2, y2>();
}
} else {
return IndexSequence<>{};
}
}
template<UInt32 x1, UInt32 y1, UInt32 x2, UInt32 y2>
static constexpr auto Region() noexcept {
return RegionImpl<x1, x1, y1, x2, y2>();
}
有效。但是,如果使用 conditional operator (a ? b : c)
而不是 if statement
,则会发生相同的错误。
使用 conditional operator
时实际发生了什么?
这种情况下的三元条件不等同于if
语句,因为它是一个constexpr if
语句。
用constexpr if
语句,
If a
constexpr if
statement appears inside a templated entity, and if condition is not value-dependent after instantiation, the discarded statement is not instantiated when the enclosing template is instantiated .
但是对于三元条件,模板总是被实例化。这导致无限递归。
请注意,如果将 constexpr if
替换为正常的 if
,则会出现相同的错误。
参见 DEMO。