我可以限制一个 C++ 函数接受特定类型的任意嵌套向量吗?

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

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&lt;T&gt;,其中T可能是另一个std::vector<U>

或者:

std::vector&lt;int&gt;,但这不接受任何嵌套的向量。

我的代码如下所示:

template&lt;class, template&lt;class...&gt; class&gt;
inline constexpr bool is_specialization = false;

template&lt;template&lt;class...&gt; class T, class... Args&gt;
inline constexpr bool is_specialization&lt;T&lt;Args...&gt;, T&gt; = true;

template&lt;class T&gt;
concept IsVector = is_specialization&lt;T, std::vector&gt;;

template&lt;class T&gt;
concept IsVectorInt = IsVector&lt;T&gt; &amp;&amp; std::is_same_v&lt;int, typename T::value_type&gt;;

template&lt;typename T&gt;
requires IsVector&lt;T&gt;
auto foo(T t)
{

}

int main()
{
    std::vector&lt;int&gt; intvec {1, 2, 3};
    std::vector&lt;std::string&gt; vecofstr {&quot;bar&quot;, &quot;baz&quot;};
    std::vector&lt;std::vector&lt;int&gt;&gt; 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&lt;T&gt;` where T could itself be another std::vector\&lt;U\&gt;

or:

`std::vector&lt;int&gt;`, 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);  // 不适用
}

Live Demo

英文:

If your goal is to detect vector or nested vector of arithmetic types, here is a proposal with trait implemented recursively:

#include &lt;iostream&gt;
#include &lt;type_traits&gt;
#include &lt;vector&gt;

template &lt;typename T&gt;
struct NestedVector : public std::false_type {};
template &lt;typename T&gt;
struct NestedVector&lt;std::vector&lt;T&gt;&gt;
    : public std::conditional_t&lt;std::is_arithmetic&lt;T&gt;::value ||
                                    NestedVector&lt;T&gt;::value,
                                std::true_type, std::false_type&gt; {};

// C++&lt;20 version
template &lt;typename T, std::enable_if_t&lt;NestedVector&lt;T&gt;::value, bool&gt; = true&gt;
void foo(T t) {}

// C++20 version
// template &lt;typename T&gt;
// requires NestedVector&lt;T&gt;::value
// void foo(T t) {}

int main() {
    std::vector&lt;int&gt; intvec{1, 2, 3};
    std::vector&lt;std::string&gt; vecofstr{&quot;bar&quot;, &quot;baz&quot;};
    std::vector&lt;std::vector&lt;int&gt;&gt; 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 &lt;typename T&gt;
struct inner_type
{
    using type = T;
};
//specialization
template &lt;typename T&gt;
struct inner_type&lt;std::vector&lt;T&gt;&gt;
{
    using type = typename inner_type&lt;T&gt;::type;
};
//alias for convenience 
template&lt;typename T&gt;
using inner_type_t = typename inner_type&lt;T&gt;::type;

template&lt;typename T&gt;
auto foo(std::vector&lt;T&gt; t)
{ 
    static_assert(std::is_arithmetic_v&lt;inner_type_t&lt;T&gt;&gt;);
}  
int main()
{
    std::vector&lt;int&gt; intvec {1, 2, 3};
    std::vector&lt;std::vector&lt;int&gt;&gt; vecofvec {{1, 2}, {1, 3}};
    foo(intvec);       //works 
    foo(vecofvec);     //works 

    std::vector&lt;std::string&gt; vecofstr{&quot;bar&quot;, &quot;baz&quot;};
    //foo(vecofstr);  //fails as expected
}

Working demo


It is trivial to to use require(or enable_if_t) instead of static_assert as shown below:

template&lt;typename T&gt;
auto foo(std::vector&lt;T&gt; t) requires(std::is_arithmetic_v&lt;inner_type_t&lt;T&gt;&gt;)
{ 
}

Demo

huangapple
  • 本文由 发表于 2023年5月25日 17:25:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76330744.html
匿名

发表评论

匿名网友

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

确定