У меня есть два варианта объектов следующего типа
struct FigureMove {};
struct PieceMove {};
using Move = std::variant<FigureMove, PieceMove>;
Я хочу предоставить два объекта Move функции и вызывать разные функции в зависимости от базовых типов в вариантах. У меня есть две разные версии функции, которая принимает объекты 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.
return areMovesValid(move0, move1);
Если параметр массива не используется, это приводит к бесконечной рекурсии для комбинации двух значений одного типа, поскольку разрешение перегрузки будет выбирать одно и то же areMovesValid.
При использовании версии параметра массива: разрешение перегрузки не выполняется, так как эти два параметра нельзя преобразовать в один std::array, а остальные перегрузки не совпадают.
Если вы решите назвать третью функцию, которая принимает пару Move как что-то отличное от areMovesValid, ни одна из версий не будет скомпилирована.
Я планирую всегда поставлять один FigureMove и один PieceMove
Это хорошо, но ваш компилятор C++ не верит вам на слово. Комбинация двух вызовов std::visit также будет генерировать пути кода, где оба параметра являются как FigureMoves, так и PieceMoves. И это должно как-то компилироваться.
Затем мне нужно написать функции, принимающие (FigureMove, FigureMove) и (PieceMove, PieceMove), и они должны нормально компилироваться? Попробую.
Да, должно компилироваться.
Следуя объяснению @SamVarshavchik и вкладу @chris, я придумал это решение:
// 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]);
}
Изменение заключается в том, что для тех же базовых типов в варианте Move мы выбрасываем исключение. Кроме того, мы отправляем через один std::visit.
К вашему сведению, std::visit поддерживает более одного варианта одновременно. Вы можете сделать один вызов с обоими вариантами для отправки, используя оба аргумента одновременно.