英文:
Can I constrain a C++ function to accept an arbitrarily nested vector of a specific type?
问题
Sure, here's the translated code portion:
我想要能够限制一个C++函数,使其只能接受以下参数:
std::vector<T>
std::vector<std::vector<T>>
std::vector<std::vector<std::vector<T>>>
...
并且,可以接受任意嵌套的std::vector,但类型必须是特定的类型T。
我希望以下代码可以编译通过:
template<typename T>
requires IsVectorInt<T>
auto foo(T t)
{
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
}
到目前为止,我已经使用概念(concepts)成功地限制了一个函数,使其只能接受以下两种参数之一:
std::vector<T>
,其中T可能是另一个std::vector<U>
或者:
std::vector<int>
,但这不接受任何嵌套的向量。
我的代码如下所示:
template<class, template<class...> class>
inline constexpr bool is_specialization = false;
template<template<class...> class T, class... Args>
inline constexpr bool is_specialization<T<Args...>, T> = true;
template<class T>
concept IsVector = is_specialization<T, std::vector>;
template<class T>
concept IsVectorInt = IsVector<T> && std::is_same_v<int, typename T::value_type>;
template<typename T>
requires IsVector<T>
auto foo(T t)
{
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::string> vecofstr {"bar", "baz"};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
foo(vecofstr); // 我希望这一行无法编译通过
}
<details>
<summary>英文:</summary>
I would like to be able to constrain a C++ function such that the only arguments it can accept are:
std::vector<T>
std::vector<std::vector<T>>
std::vector<std::vector<std::vector<T>>>
...
and so on, accepting an arbitrarily nested std::vector, but with a specific type T
I would like the following to compile:
template<typename T>
requires IsVectorInt<T>
auto foo(T t)
{
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
}
So far I have managed to constrain a function using concepts to only accept either:
`std::vector<T>` where T could itself be another std::vector\<U\>
or:
`std::vector<int>`, this however, does not accept any nested vectors.
My code is shown below:
template<class, template<class...> class>
inline constexpr bool is_specialization = false;
template<template<class...> class T, class... Args>
inline constexpr bool is_specialization<T<Args...>, T> = true;
template<class T>
concept IsVector = is_specialization<T, std::vector>;
template<class T>
concept IsVectorInt = IsVector<T> && std::is_same_v<int, typename T::value_type>;
template<typename T>
requires IsVector<T>
auto foo(T t)
{
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::string> vecofstr {"bar", "baz"};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
foo(vecofstr); // I would like this line to fail to compile
}
</details>
# 答案1
**得分**: 4
如果您的目标是检测算术类型的向量或嵌套向量,这里提供了一个使用递归实现的特性的提案:
```c++
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T>
struct NestedVector : public std::false_type {};
template <typename T>
struct NestedVector<std::vector<T>>
: public std::conditional_t<std::is_arithmetic<T>::value ||
NestedVector<T>::value,
std::true_type, std::false_type> {};
// C++<20版本
template <typename T, std::enable_if_t<NestedVector<T>::value, bool> = true>
void foo(T t) {}
// C++20版本
// template <typename T>
// requires NestedVector<T>::value
// void foo(T t) {}
int main() {
std::vector<int> intvec{1, 2, 3};
std::vector<std::string> vecofstr{"bar", "baz"};
std::vector<std::vector<int>> vecofvec{{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
// foo(vecofstr); // 不适用
}
英文:
If your goal is to detect vector or nested vector of arithmetic types, here is a proposal with trait implemented recursively:
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T>
struct NestedVector : public std::false_type {};
template <typename T>
struct NestedVector<std::vector<T>>
: public std::conditional_t<std::is_arithmetic<T>::value ||
NestedVector<T>::value,
std::true_type, std::false_type> {};
// C++<20 version
template <typename T, std::enable_if_t<NestedVector<T>::value, bool> = true>
void foo(T t) {}
// C++20 version
// template <typename T>
// requires NestedVector<T>::value
// void foo(T t) {}
int main() {
std::vector<int> intvec{1, 2, 3};
std::vector<std::string> vecofstr{"bar", "baz"};
std::vector<std::vector<int>> vecofvec{{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
// foo(vecofstr); // KO
}
(NB: I'm using C++<20 so I removed the requires but I think that it may work juste the same)
Live Demo
答案2
得分: 2
你可以通过创建一个特性来简化并使你的示例更易读,以找到向量的最内层类型,然后像下面所示使用static_assert
:
// 用于获取最内层类型的特性
template <typename T>
struct inner_type
{
using type = T;
};
// 特化
template <typename T>
struct inner_type<std::vector<T>>
{
using type = typename inner_type<T>::type;
};
// 为了方便使用的别名
template<typename T>
using inner_type_t = typename inner_type<T>::type;
template<typename T>
auto foo(std::vector<T> t)
{
static_assert(std::is_arithmetic_v<inner_type_t<T>>);
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec); // 正常工作
foo(vecofvec); // 正常工作
std::vector<std::string> vecofstr{"bar", "baz"};
// foo(vecofstr); // 预期失败
}
使用require
(或enable_if_t
)而不是static_assert
非常简单,如下所示:
template<typename T>
auto foo(std::vector<T> t) requires(std::is_arithmetic_v<inner_type_t<T>>)
{
}
英文:
You can simplify and make your example more readable by creating a trait to find the inner most type of the vector and then using a static_assert
as shown below:
//trait to get the innermost type
template <typename T>
struct inner_type
{
using type = T;
};
//specialization
template <typename T>
struct inner_type<std::vector<T>>
{
using type = typename inner_type<T>::type;
};
//alias for convenience
template<typename T>
using inner_type_t = typename inner_type<T>::type;
template<typename T>
auto foo(std::vector<T> t)
{
static_assert(std::is_arithmetic_v<inner_type_t<T>>);
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec); //works
foo(vecofvec); //works
std::vector<std::string> vecofstr{"bar", "baz"};
//foo(vecofstr); //fails as expected
}
It is trivial to to use require
(or enable_if_t
) instead of static_assert
as shown below:
template<typename T>
auto foo(std::vector<T> t) requires(std::is_arithmetic_v<inner_type_t<T>>)
{
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论