分别提供两个变体对象与将它们放入数组中的差异

huangapple go评论72阅读模式
英文:

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&lt;FigureMove, PieceMove&gt;;

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 &amp;figureMove0, const PieceMove &amp;pieceMove1)
{
    return {};
}

bool areMovesValid(const PieceMove &amp;pieceMove0, const FigureMove &amp;figureMove1)
{
    return {};
}

//#define USE_ARRAY

#ifdef USE_ARRAY
bool areMovesValid(const std::array&lt;Move, 2&gt; &amp;moves)
{
    const auto &amp;variantMove0 = moves[0];
    const auto &amp;variantMove1 = moves[1];

#else
bool areMovesValid(const Move &amp;variantMove0, const Move &amp;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 调用的组合也会生成两个参数都是 FigureMovePieceMove 的代码路径。而这必须编译,无论如何。

英文:
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 Moves 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 FigureMoves and PieceMoves. 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 &lt;stdexcept&gt;
#include &lt;type_traits&gt;

// previous code

// changes made to dispatching function
bool areMovesValid(const std::array&lt;Move, 2&gt; &amp;moves)
{
    return std::visit(
        []&lt;typename Move0, typename Move1&gt;(Move0 move0, Move1 move1) -&gt; bool
        {
            if constexpr (std::is_same_v&lt;Move0, Move1&gt;)
                throw std::invalid_argument(&quot;unsupported arguments!&quot;);
            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.

huangapple
  • 本文由 发表于 2023年2月19日 22:10:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75500710.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定