返回C ++ 17可变参数模板“构造扣除指南”的可变参数聚合(struct)和语法 [英] Returning variadic aggregates (struct) and syntax for C++17 variadic template 'construction deduction guide'

查看:140
本文介绍了返回C ++ 17可变参数模板“构造扣除指南”的可变参数聚合(struct)和语法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用下面的许多等模板结构,可以返回一组固定的可能不可移动的对象,并使用c ++ 17结构化绑定code> auto [a,b,c] = f(); 声明变量ab和c并从f返回它们的值,例如struct或tuple。

 模板< typename T1,typename T2,typename T3> 
struct many {
T1 a;
T2 b;
T3 c;
};

// guide:
template< class T1,class T2,class T3>
many(T1,T2,T3) - >许多< T1,T2,T3> ;;

auto f(){return many {string(),5.7,unmovable()}; };

int main(){
auto [x,y,z] = f();
}

正如这两个问题和答案所解释的( std :: tuple和std :: pair支持聚合初始化?
,特别是接受ecatmur的回答,还会多个返回值(结构化绑定)与不可移动类型或保证RVO在C ++ 17 ), std: :tuple 不支持聚合初始化。这意味着它不能用于保存和返回不可移动类型。但是一个简单的结构如很多可以做到这一点,这导致了一个问题:





更新:在<$ c的模板版本中, $ c> many ,将允许以下指南语法?

  template< typename Args .. 。> 
many(Args ...) - > many< Args ...> ;;


解决方案

在C ++ 17中,聚合初始化将能够初始化公共基类。所以你可以使用继承+包扩展来构建这样的类。要使它与结构化绑定一起工作,你必须暴露tuple接口:specialize std :: tuple_size std :: tuple_element 为您的类提供 get 函数:

 用于许多类实现
#include< utility>
#include< tuple>

namespace rw {
namespace detail {

template< size_t index,typename T>
struct many_holder
{T value; };

template< typename idx_seq,typename ... Types>
struct many_impl;

template< size_t ... Indices,typename ... Types>
struct many_impl< std :: index_sequence< Indices ...>,Types ...> ;: many_holder< Indices,Types> ...
{};

}

模板< typename ... Types>
struct many:detail :: many_impl< typename std :: make_index_sequence< sizeof ...(Types)> Types,>
{};

template< size_t N,typename ... Types>
auto get(const rw :: many< Types ...>& data) - > const std :: tuple_element_t< N,rw :: many< Types ...>>&
{
const rw :: detail :: many_holder< N,std :: tuple_element_t< N,rw :: many< Types ...>>>& holder = data;
return holder.value;
}

}

命名空间std {
模板< typename ... Types>
struct tuple_size< rw :: many< Types ...>> :std :: integral_constant< std :: size_t,sizeof ...(Types)>
{};

template< std :: size_t N,class ... Types>
struct tuple_element< N,rw :: many< Types ...> >
{using type = typename tuple_element< N,std :: tuple< Types ...>> :: type; };
}

//用于测试的头
#include< iostream>
#include< string>

int main()
{
rw :: many< int,std :: string,int> x = {42,Hello,11};
std :: cout<< std :: tuple_size< decltype(x)>()< '\\\
'<< rw :: get< 1>(x);
}

演示(现在仅适用于clang 3.9):
< a href =http://melpon.org/wandbox/permlink/9NBqkcbOuURFvypt =nofollow> http://melpon.org/wandbox/permlink/9NBqkcbOuURFvypt



注释




  • 在演示中,注释掉了 nth_type ,可以直接使用 tuple_element 来实现,而不是将其延迟到 tuple_element< tuple> 执行。

  • get< many> 应为 c $ c>或放在关联的命名空间中以便结构化绑定工作。你不应该重载 std :: get (这将是UB无论如何)。

  • 我离开实现 get 用于非const引用和r值引用作为读者的练习。

  • 没有使用结构化绑定和指南的示例,因为clang不支持它们(实际上我不知道任何支持它们的编译器)。理论上

    模板< typename ... Types> many(Types ...) - >


Using a template struct such as many below, it's possible to return a fixed set of possibly unmovable objects, and receive them using the c++17 structured binding (auto [a,b,c] = f(); declares the variables a b and c and assigns their value from f returning for example a struct or tuple).

template<typename T1,typename T2,typename T3>
struct many {
  T1 a;
  T2 b;
  T3 c;
};

// guide:
template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;

auto f(){ return many{string(),5.7, unmovable()}; }; 

int main(){
   auto [x,y,z] = f();
}

As explained in these two questions and answers (Do std::tuple and std::pair support aggregate initialization? and especially the accepted answer by ecatmur, also Multiple return values (structured bindings) with unmovable types or guaranteed RVO in C++17), std::tuple does not support aggregate initialization. That means that it can not be used to hold and return unmovable types. But a simple struct like many can do this, which leads to the question:

Is it possible to create a variadic version of many which works with any number of arguments?

Update: in a templated version of many, will the following guide syntax be allowed?

template<typename Args...>    
many(Args...) -> many<Args...>;

解决方案

In C++17 aggregate initialization will be able to initialize public base classes. So you can use inheritance + pack expansion to build such class. To make it work with structured bindings, you will have to expose tuple interface: specialize std::tuple_size and std::tuple_element and provide get function for your class:

//Headers used by "many" class implementation
#include <utility>
#include <tuple>

namespace rw {
    namespace detail {

    template <size_t index, typename T>
    struct many_holder
    { T value; };

    template <typename idx_seq, typename... Types>
    struct many_impl;

    template <size_t... Indices, typename... Types>
    struct many_impl<std::index_sequence<Indices...>, Types...>: many_holder<Indices, Types>...
    {};

    }

template <typename... Types>
struct many: detail::many_impl<typename std::make_index_sequence<sizeof...(Types)>, Types...>
{};

template<size_t N, typename... Types> 
auto get(const rw::many<Types...>& data) -> const std::tuple_element_t<N, rw::many<Types...>>&
{
    const rw::detail::many_holder<N, std::tuple_element_t<N, rw::many<Types...>>>& holder = data;
    return holder.value;
}

}

namespace std {
    template <typename... Types>
    struct tuple_size<rw::many<Types...>> : std::integral_constant<std::size_t, sizeof...(Types)> 
    {};

    template< std::size_t N, class... Types >
    struct tuple_element<N, rw::many<Types...> >
    { using type = typename tuple_element<N, std::tuple<Types...>>::type; };
}

//Headers used for testing
#include <iostream>
#include <string>

int main()
{
    rw::many<int, std::string, int> x = {42, "Hello", 11};
    std::cout << std::tuple_size<decltype(x)>() << '\n' << rw::get<1>(x);
}

Demo (right now works only in clang 3.9): http://melpon.org/wandbox/permlink/9NBqkcbOuURFvypt

Notes:

  • In demo there is a commented out implementation of nth_type, you can use to inplement tuple_element directly and not defer it to tuple_element<tuple> implementation.
  • get<many> should be either a member function of many or be placed in associated namespace for structured bindings to work. You should not overload std::get (It would be UB anyway).
  • I left implementation of get for non-const references and r-value references as excercise for the reader.
  • There is no example of using structured bindings and guides, because clang does not support them (in fact I do not know any compiler which supports them). In theory
    template<typename... Types> many(Types...) -> many<Types...>; should work.

这篇关于返回C ++ 17可变参数模板“构造扣除指南”的可变参数聚合(struct)和语法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆