返回C++ 17可变参数模板'构造演绎指南'的可变参数聚合(结构)和语法

Joh*_*erg 7 c++ templates c++17

使用many如下所示的模板结构,可以返回一组固定的可能不可移动的对象,并使用c ++ 17结构化绑定接收它们(auto [a,b,c] = f();声明变量ab和c并从f返回它们的值,例如结构或元组).

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();
}
Run Code Online (Sandbox Code Playgroud)

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

是否可以创建一个many可以使用任意数量参数的可变版本?

更新:在模板版本中many,是否允许使用以下指南语法?

template<typename Args...>    
many(Args...) -> many<Args...>;
Run Code Online (Sandbox Code Playgroud)

Rev*_*lot 6

在C++ 17中,聚合初始化将能够初始化公共基类.因此,您可以使用继承+包扩展来构建此类.要使其与结构化绑定一起使用,您必须公开tuple接口:specialize std::tuple_size并为您的类std::tuple_element提供get函数:

//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);
}
Run Code Online (Sandbox Code Playgroud)

演示(现在仅适用于第3.9条):http: //melpon.org/wandbox/permlink/9NBqkcbOuURFvypt

备注:

  • 在演示中有一个注释掉的实现nth_type,你可以tuple_element直接使用它而不是推迟tuple_element<tuple>实现.
  • get<many>应该是many结构化绑定的成员函数或放置在关联的命名空间中,以使结构化绑定起作用.你不应该超载std::get(无论如何都是UB).
  • get将非const引用和r值引用的实现作为阅读器的练习.
  • 没有使用结构化绑定和指南的示例,因为clang不支持它们(实际上我不知道任何支持它们的编译器).在理论上
    template<typename... Types> many(Types...) -> many<Types...>;应该工作.