c++ - 结构化绑定(bind)宽度

标签 c++ variadic-templates typetraits c++17 structured-bindings

是否可以使用结构化绑定(bind)语法来确定我应该在方括号中指定多少个变量名,以匹配普通右侧 struct 的数据成员数量?

我想制作通用库的一部分,它使用结构化绑定(bind)将任意类分解为其组成部分。目前还没有结构化绑定(bind)的可变版本(而且,我认为,不能用于当前提议的语法),但我的第一个想法是对某些函数 decompose() 进行一组重载,它将 struct 参数分解为其组成部分。 decompose() 应该由参数(即 struct)数据成员的数量重载。目前 constexpr if 语法也可用于分派(dispatch)它。但是,为了上述目的,我如何模拟类似于 sizeof... 运算符的东西呢?我不能在 SFINAE 构造中的某处使用 auto [a, b, c] 语法,因为它是分解声明 并且据我所知,任何声明都不能在 中使用decltype,我也不能在 lambda 函数体中出于我的目的使用它,因为 lambda 函数也不能在模板参数中使用。

我当然想要内置运算符(语法如 sizeof[] S/sizeof[](S) for class S) , 但像下面这样的东西也是可以接受的:

template< typename type, typename = void >
struct sizeof_struct
{

};

template< typename type >
struct sizeof_struct< type, std::void_t< decltype([] { auto && [p1] = std::declval< type >(); void(p1); }) > >
    : std::integral_constant< std::size_t, 1 >
{

};

template< typename type >
struct sizeof_struct< type, std::void_t< decltype([] { auto && [p1, p2] = std::declval< type >(); void(p1); void(p2);  }) > >
    : std::integral_constant< std::size_t, 2 >
{

};

... etc up to some reasonable arity

也许 constexpr lambda 将允许我们将它们用于模板的参数中。你怎么看?

即将到来的概念有可能吗?

最佳答案

struct two_elements {
  int x;
  double y;
};

struct five_elements {
  std::string one;
  std::unique_ptr<int> two;
  int * three;
  char four;
  std::array<two_elements, 10> five;
};

struct anything {
  template<class T> operator T()const;
};

namespace details {
  template<class T, class Is, class=void>
  struct can_construct_with_N:std::false_type {};

  template<class T, std::size_t...Is>
  struct can_construct_with_N<T, std::index_sequence<Is...>, std::void_t< decltype(T{(void(Is),anything{})...}) >>:
  std::true_type
  {};
}
template<class T, std::size_t N>
using can_construct_with_N=details::can_construct_with_N<T, std::make_index_sequence<N>>;

namespace details {
  template<std::size_t Min, std::size_t Range, template<std::size_t N>class target>
  struct maximize:
    std::conditional_t<
      maximize<Min, Range/2, target>{} == (Min+Range/2)-1,
      maximize<Min+Range/2, (Range+1)/2, target>,
      maximize<Min, Range/2, target>
    >
  {};
  template<std::size_t Min, template<std::size_t N>class target>
  struct maximize<Min, 1, target>:
    std::conditional_t<
      target<Min>{},
      std::integral_constant<std::size_t,Min>,
      std::integral_constant<std::size_t,Min-1>
    >
  {};
  template<std::size_t Min, template<std::size_t N>class target>
  struct maximize<Min, 0, target>:
    std::integral_constant<std::size_t,Min-1>
  {};

  template<class T>
  struct construct_searcher {
    template<std::size_t N>
    using result = ::can_construct_with_N<T, N>;
  };
}

template<class T, std::size_t Cap=20>
using construct_airity = details::maximize< 0, Cap, details::construct_searcher<T>::template result >;

这对 T 的最长构造空气度进行二分搜索从 0 到 20。20 是一个常量,您可以根据需要增加它,但会增加编译时和内存开销。

Live example .

如果你的结构中的数据不能从它自己类型的右值构造,它不会在 C++14 中工作,但我相信 guanteed elision 发生在 C++17 here (!)

将其转换为结构化绑定(bind)需要的不仅仅是一堆手动代码。但是一旦你知道了,你应该能够提出诸如“这个 struct 的第三种类型是什么”之类的问题。

如果 struct可以在没有 tuple_size 的情况下分解为结构化绑定(bind)事情正在做,它的通风性决定了它需要多少变量。

不幸的是std::tuple_size即使在 C++17 中也不是 SFINAE 友好的。但是,使用 tuple_size 的类型部分还需要启用 ADL std::get .

使用 failure_tag get<std::size_t>(Ts const&...) 创建命名空间那using std::get .使用它来检测它们是否已覆盖 get<0>在类型 ( !std::is_same< get_type<T,0>, failure_tag >{} ) 上,如果是这样,请往下 tuple_element确定通风的路径。将生成的元素填充到 std::tuple 中的 decltype(get<Is>(x))并归还。

如果失败,使用上面的 construct_airity ,并用它来弄清楚如何在类型上使用结构化绑定(bind)。然后我可能会将其发送到 std::tie , 为了统一。

我们现在有tuple_it它接受任何类似结构化绑定(bind)的东西并将其转换为引用或值的元组。 现在两条路径都收敛了,您的通用代码更容易了!

关于c++ - 结构化绑定(bind)宽度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39768517/

相关文章:

c++ - 更改 const 对象的数组成员的元素

c++ - 如何通过类型推导过滤可变参数模板包?

可变参数宏中的 C++ 可变参数模板函数

c++ - vector<unique_ptr> 上的 is_copy_constructible 误报

c++ - 如何使用多重继承内联虚拟

c++ - 检查嵌套的模板类

c++ - 当另一个成员存在另一个签名时,调用纯虚拟成员的正确方法是什么?

c++ - 双向链表 : Properly deleting an adding something in the middle of a list?

c++ - 合并两个 std::set 和(尚未) std::set::merge()

c++ - 可变参数模板类型特征解析