Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
303 views
in Technique[技术] by (71.8m points)

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

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 and 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...>;
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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)>() << '
' << 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.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...