返回 C++17 可变参数模板的可变参数聚合(结构)和语法 'construction deduction guide'
Returning variadic aggregates (struct) and syntax for C++17 variadic template 'construction deduction guide'
使用模板结构,例如下面的 many
,可以 return 一组固定的可能不可移动的对象,并使用 c++17 结构化绑定(auto [a, b, c] = f();
声明变量 a
、b
和 c
并从 f
return 中分配它们的值,例如结构或元组)。
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();
}
正如这两个问题和答案中所解释的(
尤其是 , also ), std::tuple
不支持聚合初始化。这意味着它不能用于保存和 return 不可移动的类型。但是像 many
这样的简单结构可以做到这一点,这导致了问题:
是否可以创建适用于任意数量参数的 many
的可变版本?
更新:在 many
的模板化版本中,是否允许使用以下指南语法?
template<typename Args...>
many(Args...) -> many<Args...>;
在 C++17 中聚合初始化将能够初始化 public 基 classes。所以可以用继承+包扩展的方式来构建这样的class。要使其与结构化绑定一起使用,您将必须公开元组接口:专门化 std::tuple_size
和 std::tuple_element
并为您的 class:
提供 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
实现留作 reader 的练习。
- 没有使用结构化绑定和指南的示例,因为 clang 不支持它们(事实上我不知道有哪个编译器支持它们)。理论上
template<typename... Types> many(Types...) -> many<Types...>;
应该可以。
前几天有一个关于 std-proposals 的 discussion about this。
我们还没有最终措辞,也没有支持演绎指南的编译器(据我所知),但根据 Richard Smith 的说法,以下演绎指南应该有效(精确):
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>
所以,是的,聚合的可变参数推导指南也应该适用,并且适用于聚合初始化语法。如果没有演绎指南,它将无法工作,因为没有演绎指南,编译器需要一个构造函数。
使用模板结构,例如下面的 many
,可以 return 一组固定的可能不可移动的对象,并使用 c++17 结构化绑定(auto [a, b, c] = f();
声明变量 a
、b
和 c
并从 f
return 中分配它们的值,例如结构或元组)。
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
不支持聚合初始化。这意味着它不能用于保存和 return 不可移动的类型。但是像 many
这样的简单结构可以做到这一点,这导致了问题:
是否可以创建适用于任意数量参数的 many
的可变版本?
更新:在 many
的模板化版本中,是否允许使用以下指南语法?
template<typename Args...>
many(Args...) -> many<Args...>;
在 C++17 中聚合初始化将能够初始化 public 基 classes。所以可以用继承+包扩展的方式来构建这样的class。要使其与结构化绑定一起使用,您将必须公开元组接口:专门化 std::tuple_size
和 std::tuple_element
并为您的 class:
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
实现留作 reader 的练习。 - 没有使用结构化绑定和指南的示例,因为 clang 不支持它们(事实上我不知道有哪个编译器支持它们)。理论上
template<typename... Types> many(Types...) -> many<Types...>;
应该可以。
前几天有一个关于 std-proposals 的 discussion about this。
我们还没有最终措辞,也没有支持演绎指南的编译器(据我所知),但根据 Richard Smith 的说法,以下演绎指南应该有效(精确):
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>
所以,是的,聚合的可变参数推导指南也应该适用,并且适用于聚合初始化语法。如果没有演绎指南,它将无法工作,因为没有演绎指南,编译器需要一个构造函数。