无法将枚举值传递给递归模板 (C++)

Can't Pass Enum Value to Recursive Template (C++)

本文关键字:C++ 递归 枚举 值传      更新时间:2023-10-16

假设我想编写一个递归模板函数,该函数将单个值与n维向量中的每个元素进行比较,如果至少有一个匹配项,则返回true,如果没有匹配项,则返回false。

我写了一些代码来做到这一点,尽管它可能远非最佳:

template <typename T, typename Checker>
void check_for_each(const T& v, const Checker condition)
{
condition(v);
}
template <typename T, typename Checker>
void check_for_each(const std::vector<T>& v, const Checker condition)
{
for(unsigned int i = 0; i < v.size(); i++)
{
check_for_each(v[i], condition);
}
}
template <typename T, typename U>
bool is_equal_any(const T& VALUE, const std::vector<typename U> VECTOR)
{
bool is_equal = false;
check_for_each(VECTOR, [&is_equal, &VALUE](const T& val)
{
if(!is_equal && VALUE == val)
{
is_equal = true;
}
});
return is_equal;
}

虽然这似乎有效,但我遇到了一个不寻常的问题,我不太理解它。 例如,以下代码有效:

enum PIECE_NAME {EMPTY, PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING};
std::vector<std::vector<int>> board {{ROOK, BISHOP}, {KNIGHT, QUEEN}};
std::cout << is_equal_any(2, board); // outputs 1 (there is a rook on the board)

然而,以下细微的变化并没有:

std::cout << is_equal_any(ROOK, board); // compile error C2664

显然我的函数无法弄清楚如何将枚举值转换为整数。 当然,我只能使用static_cast<int>(ROOK),代码按预期编译和运行,但这显然并不理想。 此外,我知道我可以将董事会的声明更改为std::vector<std::vector<PIECE_NAME>> board,(它也按预期运行(,但我宁愿将其保留在int.那么是否可以重写这些递归模板函数,以便is_equal_any可以直接获取枚举值呢?我对C++仍然很陌生,所以我真的很感激你的回答中尽可能多的细节。 谢谢。

问题来自这里的类型T

check_for_each(VECTOR, [&is_equal, &VALUE](const T& val)
^

通过调用

is_equal_any(ROOK, board)

T是一个PIECE_NAME,但你最终作为参数传递给这个lambda的是你的向量的int类型的元素。但是int不能隐式转换为枚举。

您不能直接使用U因为它可能是std::vector<int>std::vector< std::vector<int> >,或者......

如果您使用的是 C++14,则可以使用带有auto的通用 lambda

check_for_each(VECTOR, [&is_equal, &VALUE](const auto& val)

但是,当您将问题标记为C++11时,您可以使用一个特征:

template <typename T>
struct leaf_type {
using type = T;
};
template <typename T>
struct leaf_type<std::vector<T>> {
using type = typename leaf_type<T>::type;
};
template <typename T>
using leaf_type_t = typename leaf_type<T>::type;

用法:

check_for_each(VECTOR, [&is_equal, &VALUE](const leaf_type_t<U> & val)

演示

顺便说一句,您应该避免嵌套std::vectors并将其线性化为单个,例如:

std::vector<int> board {ROOK, BISHOP, KNIGHT, QUEEN};

然后你可以很容易地使用std::find。

这是一个XY问题,因为有更好的方法:

  • 使用作用域枚举
    • 不要混合使用整数和枚举
  • 将您的工作委托给std::any_of

例如:

namespace multi_dim{
template< class InputIt, class UnaryPredicate >
bool any_of(InputIt first, InputIt last, UnaryPredicate p)
{
using std::any_of;
for(;first != last; ++first)
{
bool next = any_of(first->cbegin(), first->cend(), p);
if (next)
return true;
}
return false;
}
}

演示

测试:

std::vector<std::vector<PIECE>> board {{PIECE::ROOK, PIECE::BISHOP}, {PIECE::KNIGHT, PIECE::QUEEN}};
std::cout << std::boolalpha << multi_dim::any_of(board.cbegin(), board.cend(), [](PIECE p){return p == PIECE::ROOK;}) << std::endl;
std::cout << std::boolalpha << multi_dim::any_of(board.cbegin(), board.cend(), [](PIECE p){return p == PIECE::EMPTY;}) << std::endl;

输出:

真假

疯狂猜测:尝试为 VALUE 和 VECTOR 的元素类型使用差异模板参数。

我不使用 MSVC,所以我不确定您到底遇到了什么错误。

。不管怎样,我只需要重复我的评论:请不要写这种代码。

尽管使用std::any_of的解决方案是最好的解决方案,但我给出的答案显示对原始代码的改进不那么剧烈。

template <typename T, typename Checker>
bool check_for_each(const T& v, const Checker condition)
{
return condition(v);
}
template <typename T, typename Checker>
bool check_for_each(const std::vector<T>& v, const Checker condition)
{
return std::find_if(begin(v), end(v), [condition](const T &t) { return check_for_each(t, condition); }) != v.end();
}
template <typename T, typename U>
bool is_equal_any(const T& value, const U &container)
{
return check_for_each(container, [&value](const T& val){ return value == val; });
}
enum class PIECE_NAME { EMPTY, PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING };
void test()
{
std::vector<std::vector<PIECE_NAME>> board
{ 
{ PIECE_NAME::ROOK, PIECE_NAME::BISHOP },
{ PIECE_NAME::KNIGHT, PIECE_NAME::QUEEN } 
};
std::cout << is_equal_any(PIECE_NAME::ROOK, board);
}

此解决方案仍然是针对嵌套向量的硬编码,但代码已简化并进行了优化,因为一旦找到项目,它将停止搜索。

正如其他评论中已经建议的那样,您确实应该使用enum(或者更好的是enum class(。在向量中存储整数没有多大意义,因为您丢失了类型信息。