【问题标题】:How can I define a concept that is satisfied by an arbitrary std::vector?如何定义任意 std::vector 满足的概念?
【发布时间】:2021-10-01 22:29:43
【问题描述】:

我想要一个concept 需要一个任意向量作为返回类型:

template<typename T>
concept HasVector = requires (T t) {
    { T.vec() } -> std::same_as<std::vector<int>>; //works
    { T.vec() } -> std::same_as<std::vector<foo>>; //want to put something arbitrary in here
}

这样我们就会有如下内容:

class A {
std::vector<int> vec() { /* ... */}
}

class B {
std::vector<double> vec() { /* ... */}
}

static_assert(HasVector<A>);
static_assert(HasVector<B>);

此外,如果需要一个向量作为返回类型,其值类型满足其他一些概念,那就更好了,即


template<typename T>
concept Arithmetic = // as in the standard

template<typename T>
concept HasArithmeticVector = requires (T t ) {
    { T. vec() } -> std::same_as<std::vector<Arithmetic>>;

有没有办法把它放在概念名称中?

【问题讨论】:

  • 如果只有template&lt;typename T, concept C=Anything&gt; concept HasVector = requires 可以存在。

标签: c++ templates vector c++20 c++-concepts


【解决方案1】:

我们首先编写一个变量模板来检查一个类型是否特化了一个模板:

template <typename T, template <typename...> class Z>
inline constexpr bool is_specialization_of = false;

template <template <typename...> class Z, class... Args>
inline constexpr bool is_specialization_of<Z<Args...>, Z> = true;

我们可以把它变成一个概念:

template <typename T, template <typename...> class Z>
concept Specializes = is_specialization_of<T, Z>;

然后我们可以用它来实现另一个概念:

template<typename T>
concept HasVector = requires (T t) {
    { t.vec() } -> Specializes<std::vector>;
};

如果你想再做进一步的检查,那只是增加了更多的要求。

template<typename T>
concept HasVector = requires (T t) {
    { t.vec() } -> Specializes<std::vector>;

    // or something along these lines
    requires Arithmetic<decay_t<decltype(t.vec()[0])>>;
    requires Arithmetic<range_value_t<decltype(t.vec())>>;
    // etc.
};

【讨论】:

  • Specializes 延伸了概念的预期设计规则;他们应该描述一个概念,而不仅仅是测试语法;并且将“这个类专门化其他类”作为一个概念有点牵强。没有?
  • @Yakk-AdamNevraumont 另外,Specializes(和基础is_specialization_of)目前不能完全通用。
  • @Deduplicator 明确地说,Specializes&lt;std::array&gt; 不起作用,实际上也不能。你可以做SpecializesTs&lt;std::vector&gt;SpecializesTA&lt;std::array&gt; 类型的愚蠢。
【解决方案2】:
#include <concepts>
#include <vector>

template<typename T>
concept Arithmetic = std::integral<T> || std::floating_point<T>;

template<typename T>
concept HasVector = requires (T t) {
  []<Arithmetic U, typename A>(std::vector<U,A> const&){}(t.vec());
};

Demo.

【讨论】:

  • 这可能接受一个类型有转换运算符到向量?
  • @MarcGlisse。不,那是impossible
  • 呃,你是对的。它确实检测到一个派生自 std::vector 的类(struct Vec:std::vector&lt;int&gt;{};)。
  • @Yakk-AdamNevraumont 已修复 :-)
【解决方案3】:

只要vec()是公共类方法:

template<typename T> struct is_vector { std::false_type operator()(); };

template<typename T, typename A> struct is_vector<std::vector<T, A>> {
    std::true_type operator()();
};

template<typename T>
concept HasVector = requires(T t) {
    { is_vector<decltype(t.vec())>{}() } -> std::same_as<std::true_type>;
};

class A {
public:
    std::vector<int> vec() {return {}; }
};

class B {
public:
    std::vector<double> vec() {return {}; }
};

class C {
};

static_assert(HasVector<A>);
static_assert(HasVector<B>);

【讨论】:

  • 这是编写类型特征的不好方法(约定是 is_vector 继承自 true_typefalse_type,而不是它提供调用运算符??)检查类型特征的不好方法(即使使用调用运算符,您也可以直接检查结果,您不必通过将类型限制为 true_type 的这一步骤 - 当它是 @987654334 时@ 甚至都不重要)。
  • is_vector 不继承任何东西,或提供任何呼叫运算符。
  • 我知道它不会继承任何东西——这就是为什么我说约定是它应该继承的。但是你的意思是它不提供任何呼叫运算符 - 这就是你的代码所做的。它有一个呼叫操作员。 operator() 就是这样。
  • 为了更清楚,@SamVarshavchik,您的代码有效,但它看起来像一个连接到c++11 decltype 解决方案的c++03 解决方案嫁接到c++20 功能。 c++11 样式代码不是很好;例如,将{ std::true_type operator()(); }; 替换为:std::true_type {};。但是,即使我们清理了您解决方案中的 c++11 部分......如果我们有 c++20,为什么不以更清洁的方式使用 c++20 呢?
  • @SamVarshavchik "... 现在必须使用std::derived_from 的服务" 不,你不需要。你以前从未使用过类型特征吗?您只需检查is_vector&lt;T&gt;::value。您的答案不是“更基本”——实际上涉及更多。
猜你喜欢
  • 2020-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-14
  • 2023-01-11
  • 1970-01-01
相关资源
最近更新 更多