1
\$\begingroup\$

Handy wrapper for aggregates to use with emplace*() container's member functions:

#include <type_traits>
#include <utility>

template< typename type >
struct aggregate_wrapper
    : type
{

    aggregate_wrapper() = default;

    template< typename ...arguments,
              bool is_noexcept = noexcept(::new (std::declval< void * >()) type{std::declval< arguments >()...}) >
    constexpr
    aggregate_wrapper(arguments &&... _arguments) noexcept(is_noexcept)
        : type{std::forward< arguments >(_arguments)...}
    { ; }

};

#include <vector>

#include <cassert>
#include <cstdlib>

int main()
{
    struct A {};
    struct B {};
    struct S { A & a; B & b; };
    A a; B b;
    std::vector< aggregate_wrapper< S > > v;
    v.emplace_back(a, b);
    S & s = v.back(); // implicitly convertible to S
    assert(&s.a == &a);
    assert(&s.b == &b);
    v.pop_back();
    v.push_back({a, b}); // (implicitly?) copy-constructible
    assert(&v.back().a == &a);
    assert(&v.back().b == &b);
    return EXIT_SUCCESS;
}

aggregate_wrapper< T > mimics T in sense of special functions available (copy/move-constructors/assignment operators and destructor). All public data memebers are available via operator . as well.

It is possible to provide incomplete list of public data member initializers to constructor, but in such a case compiler's warning will be here (default data member initializers for omitted data members can solve the problem (C++14 feature)).

There is very comprehensive test for this wrapper.

What are there design flaws (maybe there is issue with explicit?)?

\$\endgroup\$
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Jamal Jan 25 '16 at 18:52
1
\$\begingroup\$

Here is a revision to your solution. The code is quite self-documenting. So, I don't bother going over it literally. Please comment if you have any questions. Original version of the code can be found here with unit test code here.

/// Wraps aggregates and provides a variadic forwarding constructor.
/// Enables in-place construction. Value initializes the aggregate
/// when default constructed.
template <typename T>
class aggregate_wrapper : public T {
private:
  using base = T;

public:
  using aggregate_type = T;

  // no `explicit`; aggregate initialization can be done implicitly;
  // be consistent with that
  template <typename... Ts>
  aggregate_wrapper(Ts&&... xs)
      : base{std::forward<Ts>(xs)...} {
    // nop
  }
};

template <typename T, std::size_t n>
class aggregate_wrapper<T[n]> {
private:
  using array = T[n];

public:
  using aggregate_type = T[n];

  template <typename... Ts>
  aggregate_wrapper(Ts&&... xs)
      : arr_{std::forward<Ts>(xs)...} {
    // nop
  }
  aggregate_wrapper(const array& arr) {
    std::copy(arr, arr + n, arr_);
  }
  aggregate_wrapper(array&& arr) {
    std::move(arr, arr + n, arr_);
  }

  operator T* () {
    return arr_;
  }
  operator const T* () const {
    return arr_;
  }

private:
  array arr_;
};

// you won't want to use `std::decay_t`;
// consider the case when `T` is an array type
template <typename T>
aggregate_wrapper<std::remove_const_t<std::remove_reference_t<T>>>
wrap_aggregate(T&& x) {
  return std::forward<T>(x);
}

I once wanted to write

template <typename T,
          typename = std::eanble_if_t<std::is_aggregate<T>::value>>
aggregate_wrapper<std::remove_const_t<std::remove_reference_t<T>>>
wrap(T&& x) {
  return std::forward<T>(x);
}

Alas, I don't know how to implement this std::is_aggregate thing (see here).

I have a feeling that you will like this. Hope you could join the party and play together ;-]

\$\endgroup\$
  • \$\begingroup\$ What about type traits? \$\endgroup\$ – Orient Jan 25 '16 at 18:40
  • \$\begingroup\$ @Orient <type_traits> doesn't help :( \$\endgroup\$ – Lingxi Jan 25 '16 at 18:42
  • \$\begingroup\$ coliru.stacked-crooked.com/a/c62c467acf097653 I mean this shortcoming. \$\endgroup\$ – Orient Jan 25 '16 at 18:46
  • \$\begingroup\$ @Orient W<A> v; won't compile. Is this a bug of the standard library implementation or what? I'm confused. Also, you are not supposed to pass non-aggregate types to T. \$\endgroup\$ – Lingxi Jan 25 '16 at 18:55
  • \$\begingroup\$ You defenitely need enabler (in forwarding constructor declaration) to forward properties of base class' constructors. In C++11 it is possible to propagate noexcept specifier using noexcept operator. Why do you ignore all I wrote? \$\endgroup\$ – Orient Jan 25 '16 at 18:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.