ARTICLE AD BOX
Three things come to mind:
1. Deduction
When invoking (overloaded) template functions, template argument deduction will deduce the free types. Presumably, you're already aware of this part (because you are using variadics in your example).
c++17 CTAD extends this to constructors of types, e.g. std::vector { 1,2,3 } becomes std::vector<int, std::allocator<int> >, std::array { 1,2,3 } becomes std::array<int, 3>.
2. Template Aliases:
You can either use template aliases:
template <typename K, typename V, typename Pair = std::pair<K const, V> > using MyMap = boost::container::flat_map<K, V, std::less<K>, boost::container::small_vector<Pair, 10> >;Now you can use the unwieldy type name as
MyMap<std::string, double> m;3. Opaque types / Concepts
In reality, you don't usually care about all the subsidiary details. For example, you might only require a "Range", or a "Container" or a "Map", not some specific instance.
Instead of
template <typename TA, size_t NA> struct Address { std::array<TA, NA> details; }; template <typename TO, size_t NO> struct Owner { std::array<TO, NO> details; }; template <typename... Ts> struct HouseRoomsLayout { std::tuple<Ts...> data_; }; template <typename TA, size_t NA, typename TO, size_t NO, typename... Ts> struct House { Address<TA, NA> address_; Owner<TO, NO> owner_; HouseRoomsLayout<Ts...> layout_; };You could write
template <typename TA, size_t NA> struct Address { std::array<TA, NA> details; }; template <typename TO, size_t NO> struct Owner { std::array<TO, NO> details; }; template <typename... Ts> struct HouseRoomsLayout { std::tuple<Ts...> data_; HouseRoomsLayout(Ts const&... room) : data_(room...) {} }; template <class T, class... U> Address(T, U...) -> Address<T, 1 + sizeof...(U)>; template <class T, class... U> Owner(T, U...) -> Owner<T, 1 + sizeof...(U)>; template <typename Address, typename Owner, typename Layout> struct House { Address address_; Owner owner_; Layout layout_; }; auto create_house_from(auto address, auto owner, auto... rooms) { std::cout << __PRETTY_FUNCTION__ << std::endl; return House{address, owner, HouseRoomsLayout{std::move(rooms)...}}; } int main() { Address addr{"Hello Inc"s, "World Drive 42", "Cincinatti", "Uganda"}; Owner owner{999}; // imagining an ID type here struct Room { std::vector<int> furniture; } backroom; struct Garage { std::vector<int> tools; } garage; HouseRoomsLayout layout{ backroom, garage }; auto h = create_house_from(addr, owner, layout); return 0; }Better yet, you wouldn't need create_XXX_from anymore, since that's what CTAD was invented for:
auto h = House{addr, owner, layout};creates the exact same house. In fact, go ham!
#include <array> #include <string> #include <tuple> #include <vector> using namespace std::string_literals; template <typename TA, size_t NA> struct Address { std::array<TA, NA> details; }; template <typename TO, size_t NO> struct Owner { std::array<TO, NO> details; }; template <typename... Ts> struct HouseRoomsLayout { std::tuple<Ts...> data_; HouseRoomsLayout(Ts const&... room) : data_(room...) {} }; template <class T, class... U> Address(T, U...) -> Address<T, 1 + sizeof...(U)>; template <class T, class... U> Owner(T, U...) -> Owner<T, 1 + sizeof...(U)>; template <typename Address, typename Owner, typename Layout> struct House { Address address_; Owner owner_; Layout layout_; }; int main() { struct Room { std::vector<int> furniture; } backroom; struct Garage { std::vector<int> tools; } garage; auto h = House{ Address{"Hello Inc"s, "World Drive 42", "Cincinatti", "Uganda"}, Owner{999}, HouseRoomsLayout{backroom, garage}, }; }I'm assuming you're learning about things here. The presented design does not seem like good interface design. At the very least, if you need all that flexibility, make concepts for the different classes of types.
