英文:
Difference in supplying two variant objects separately vs in an array
问题
我有以下类型的两个变体对象
struct FigureMove {};
struct PieceMove {};
using Move = std::variant<FigureMove, PieceMove>;
我想向一个函数提供两个 Move
对象,并根据变体中的基础类型调用不同的函数。
我有两个版本的函数,一个将它们作为单独的函数参数,另一个将它们作为一个 array
中的两个参数。
请注意,我计划始终提供一个 FigureMove
和一个 PieceMove
,只是它们的顺序事先不清楚。
bool areMovesValid(const FigureMove &figureMove0, const PieceMove &pieceMove1)
{
return {};
}
bool areMovesValid(const PieceMove &pieceMove0, const FigureMove &figureMove1)
{
return {};
}
//#define USE_ARRAY
#ifdef USE_ARRAY
bool areMovesValid(const std::array<Move, 2> &moves)
{
const auto &variantMove0 = moves[0];
const auto &variantMove1 = moves[1];
#else
bool areMovesValid(const Move &variantMove0, const Move &variantMove1)
{
#endif
return std::visit(
[variantMove1](const auto move0)
{
return std::visit(
[move0](const auto move1)
{
return areMovesValid(move0, move1);
},
variantMove1);
},
variantMove0);
}
以 array
为参数的版本会产生大量编译时错误。使用 gcc 或 clang。
为什么会这样,我该如何修复?
这里是在 godbolt 上的代码。
英文:
I have two variant objects of the following type
struct FigureMove {};
struct PieceMove {};
using Move = std::variant<FigureMove, PieceMove>;
I want to supply two Move
objects to a function and call different functions depending on the underlying types in the variants.
I have two different versions of the function which takes the Move
objects. One taking them as separate function arguments and one taking both of them in an array
.
Note, I plan on supplying always one FigureMove
and one PieceMove
, just their order is not clear beforehand.
bool areMovesValid(const FigureMove &figureMove0, const PieceMove &pieceMove1)
{
return {};
}
bool areMovesValid(const PieceMove &pieceMove0, const FigureMove &figureMove1)
{
return {};
}
//#define USE_ARRAY
#ifdef USE_ARRAY
bool areMovesValid(const std::array<Move, 2> &moves)
{
const auto &variantMove0 = moves[0];
const auto &variantMove1 = moves[1];
#else
bool areMovesValid(const Move &variantMove0, const Move &variantMove1)
{
#endif
return std::visit(
[variantMove1](const auto move0)
{
return std::visit(
[move0](const auto move1)
{
return areMovesValid(move0, move1);
},
variantMove1);
},
variantMove0);
}
The version taking the array
throws tons of compile time errors. Using gcc or clang.
Why is that and how can I fix it?
Here is the code on godbolt.
答案1
得分: 1
return areMovesValid(move0, move1);
不使用数组参数时,对于两个相同类型的值的组合,这会导致无限递归,因为重载决议将选择相同的 areMovesValid
。
使用数组参数版本时:重载决议失败,因为这两个参数无法转换为单个 std::array
,而剩下的重载不匹配。
如果选择为第三个函数命名,即接受一对 Move
的函数不叫 areMovesValid
,则两个版本都不会编译。
我计划始终提供一个
FigureMove
和一个PieceMove
这很好,但你的 C++ 编译器并不会盲目相信你的话。两个 std::visit
调用的组合也会生成两个参数都是 FigureMove
和 PieceMove
的代码路径。而这必须编译,无论如何。
英文:
return areMovesValid(move0, move1);
When not using an array parameter this results in infinite recursion for a combination of two values that are the same type, because overload resolution will pick the same areMovesValid
.
When using an array parameter version: overload resolution fails, since these two parameters cannot be converted to a single std::array
, and the remaining overloads don't match.
If you choose to name the third function, the one that takes a pair of Move
s as something other than areMovesValid
, neither version will compile.
> I plan on supplying always one FigureMove
and one PieceMove
That's nice, but your C++ compiler is not taking your word for it. The combination of the two std::visit
calls will also generate the code paths where both parameters are both FigureMove
s and PieceMove
s. And that has to compile, somehow.
答案2
得分: 0
由@SamVarshavchik的解释和@chris的输入,我想出了这个解决方案:
// additional headers
#include
#include <type_traits>
// previous code
// changes made to dispatching function
bool areMovesValid(const std::array<Move, 2> &moves)
{
return std::visit(
[]<typename Move0, typename Move1>(Move0 move0, Move1 move1) -> bool
{
if constexpr (std::is_same_v<Move0, Move1>)
throw std::invalid_argument("unsupported arguments!");
else
return areMovesValid(move0, move1);
},
moves[0], moves1);
}
更改的部分是,对于`Move`变体中相同的基础类型,我们会抛出异常。此外,我们通过单个`std::visit`进行调度。
英文:
Following the explanation by @SamVarshavchik and input by @chris I came up with this solution:
// additional headers
#include <stdexcept>
#include <type_traits>
// previous code
// changes made to dispatching function
bool areMovesValid(const std::array<Move, 2> &moves)
{
return std::visit(
[]<typename Move0, typename Move1>(Move0 move0, Move1 move1) -> bool
{
if constexpr (std::is_same_v<Move0, Move1>)
throw std::invalid_argument("unsupported arguments!");
else
return areMovesValid(move0, move1);
},
moves[0], moves[1]);
}
The change is that for same underlying types in the Move
variant we throw an exception.
Furthermore, we dispatch via a single std::visit
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论