返回 C++17 可变参数模板'construction deduction guide'的可变参数聚合(结构)和语法

Returning variadic aggregates (struct) and syntax for C++17 variadic template 'construction deduction guide'

本文关键字:参数 变参 结构 语法 construction C++17 返回 guide deduction      更新时间:2023-10-16

使用模板结构,如下many,可以返回一组固定的可能不可移动的对象,并使用 c++17 结构化绑定接收它们(auto [a, b, c] = f(); 声明变量abc,并从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();
}

正如这两个问题和答案中所解释的(std::tuple 和 std::p air 是否支持聚合初始化?尤其是 ecatmur 接受的答案,还有具有不可移动类型和保证 RVO 的 C++17) 的多个返回值(结构化绑定),std::tuple不支持聚合初始化。这意味着它不能用于保存和返回不可移动的类型。但是像many这样的简单结构可以做到这一点,这就引出了问题:

是否可以创建适用于任意数量参数的many的可变参数版本?

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

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

在 C++17 中,聚合初始化将能够初始化公共基类。所以你可以使用继承+包扩展来构建这样的类。要使其与结构化绑定一起使用,您必须公开元组接口:专门化std::tuple_sizestd::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);
}

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

注释

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

就在前几天,关于性病提案的讨论。

我们还没有最终的措辞,或者就此而言,支持演绎指南的编译器(我知道),但根据理查德史密斯的说法,以下演绎指南应该可以工作(precis'd):

template<class A, class B>
struct Agg
{
    A a;
    B b;
};
template<class A, class B>
Agg(A a, B b) -> Agg<A, B>;
Agg agg{1, 2.0}; // deduced to Agg<int, double>
所以是的,聚合

的可变参数推导指南也应该有效,并且可以与聚合初始化语法一起使用。没有演绎指南,将无法工作,因为没有演绎指南,编译器需要一个构造函数。