首页 > 解决方案 > 用于检查容器类中是否存在函数的 C++ 概念和隐式推导规则

问题描述

我试图弄清楚如何做一些对我来说似乎很棘手的事情,比如 std::vector 之类的概念和模板类型。

  1. 我正在尝试应用类似于我在 T 上使用 std::movable 的编译时间约束,但在 C 上使用 PushBackMovable。它适用于函数 decl 尾部的需求,但我希望保持一致并将我的约束放在模板 args 中。我尝试用“PushBackMovable C”替换“C 类”,但这并没有那么严重地失败,但更接近我想要的。

  2. 我正在尝试在模板化模板参数中使用模板类型。
    2a. 有没有办法只声明 C 并在函数签名中使用其中的模板参数?例如,我可以删除“T”和“Alloc”,并使用“_T”和“_Alloc”吗?似乎我无法访问这些参数。我想节省一些代码空间。
    2b。他们是一种在运算符的第一个参数上删除 C 上的空 <> 的方法吗?
    2c。如果我只有 PushBackMovable,编译器可以根据需要推断出 C 的模板类型,但是然后 declval barfs。我是否缺少任何技巧来隐式确定模板参数,特别是在“C”实例上?最好只说“C”。

  3. 他们是否比下面更容易检查方法的存在?

这是我在这两种情况下的示例代码:

#include <vector>
#include <type_traits>
#include <algorithm>

template<typename T, typename Alloc, template<typename _T=T, typename _Alloc=Alloc> class C >
concept PushBackMovable = std::is_same_v<decltype(std::declval<C<T,Alloc> >().push_back(std::move(T{}))),void> &&
                          std::is_same_v<decltype(std::declval<C<T,Alloc> >().push_back(T{})),void>; 

template<std::movable T, typename Alloc, template<typename _T=T, typename _Alloc=Alloc> class C > 
void operator+=( C<>& lhs, T rhs ) requires PushBackMovable<T, Alloc, C>
{
  lhs.push_back( std::forward<T>( rhs ) );
}

int main() {
  std::vector<int> ints;
  int a = 5;

  ints += 1;
  ints += a;

  std::copy(std::begin(ints), std::end(ints), std::ostream_iterator<int>(std::cout, " "));
}

谢谢您的帮助。

标签: c++templatestemplate-meta-programmingc++20c++-concepts

解决方案


通过使用模板模板参数,您已经将您的函数约束到遵循标准库模式的容器以及恰好具有两个类型模板参数的类模板。这是一个强约束,在实践中通常无用。也就是说,我建议您改为对由类型模板参数表示的具体类型进行操作,并让概念验证您想要强加的任何要求。

此外,不要使用decltypewith std::declvalwithstd::is_same来检查表达式的有效性。概念为此目的有一个专门的语法,就是把这个表达式放在大括号中。

一种解决方案是将您要验证的内容合并到一个概念中,如下所示:

#include <concepts>
#include <utility>
#include <type_traits>

template <typename C, typename T>
concept PushBackMovable = requires (C c, T t) {
    { c.push_back(t) } -> std::same_as<void>;
    { c.push_back(std::move(t)) } -> std::same_as<void>;
};

template <typename T, PushBackMovable<std::remove_reference_t<T>> C>
void operator+=(C& lhs, T&& rhs)
{
    lhs.push_back(std::forward<T>(rhs));
}

演示


推荐阅读