首页 > 解决方案 > 一个自包含的 C++ 概念可以匹配一个带有任何参数的特定模板吗?

问题描述

无论模板参数之一如何,我都想要一个匹配特定模板类型的 C++ 概念。当然,我可以使用其他一些辅助声明来区分模板类型。但是特别是概念和需求表达式的好处之一是它们消除了对辅助类型的许多需求。所以我想知道是否有可能通过一个概念声明来做到这一点“一体化”。

这是一个最小的工作示例:

#include <algorithm>
#include <compare>
#include <iostream>
#include <memory>
#include <string>

template<typename A> using char_string =
  std::basic_string<char, std::char_traits<char>, A>;

using std::string;

template<typename T> struct AltAllocator : std::allocator<T> {};
using AltString = char_string<AltAllocator<char>>;

template<typename T> constexpr bool is_char_string_v = false;
template<typename A> constexpr bool is_char_string_v<char_string<A>> = true;

template<typename T> concept is_char_string = is_char_string_v<T>;

inline bool
operator==(is_char_string auto const &a, is_char_string auto const &b)
{
  return std::equal(a.begin(), a.end(), b.begin(), b.end());
}

int
main()
{
  string s;
  AltString as;
  std::cout << std::boolalpha << (s == as) << std::endl;
}

我希望能够在is_char_string无需介绍的情况下进行定义is_char_string_v。在这种特殊情况下,知道字符串包含它们的分配器类型,我当然可以用这样的东西“作弊”:

template<typename T> concept is_char_string =
  std::same_as<T, char_string<typename T::allocator_type>>;

有没有更通用的方法来编写一个独立的概念来匹配使用任何模板参数实例化的某些特定模板?

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

解决方案


我不知道更广泛的问题可能是什么,但我们可以将检查某物是否为 a 的问题分解basic_string<char, char_traits<char>, A>为以下问题:(1) 它是 abasic_string和 (2) 它的前两种类型是charand char_traits<char>

第一个问题是标准is_specialization_of特征:

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

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

第二个我们可以使用Boost.Mp11进行一般类型列表操作。所以要么:

template <typename T>
concept char_string = is_specialization_of<T, std::basic_string>
                   && std::same_as<mp_first<T>, char>
                   && std::same_as<mp_second<T>, std::char_traits<char>>;

或者将后两者一起检查:

template <typename T>
concept char_string = is_specialization_of<T, std::basic_string>
                   && std::same_as<
                           mp_take_c<T, 2>
                           std::basic_string<char, std::char_traits<char>
                       >;

在这里只取前两个参数是安全的,因为第三个是默认的。并且由于第二个也是默认设置,因此您可以改为比较 tostd::string而不是std::basic_string<char, std::char_traits<char>.


如果意图真的是避免任何依赖关系或任何其他类型,那么我想你可以这样写:

template <typename T>
concept char_string = requires (T t) {
    []<typename A>(std::basic_string<char, std::char_traits<char>, A>){}(t);
};

但我不确定这是个好主意。


推荐阅读