c++classpointersreferenceclass-members

Pass reference argument to STL container element constructor


Very often in C++ I want an STL container of a type which has a reference member:

struct C {};

struct S
{
    S() = default;

    S(const C& c) : _c(c)
    {}

    const C& _c;   // I always have to use a pointer here
};

int main()
{
    C c;
    std::array<S, 10> arr{ c };   // Can c be passed here?

    return 0;
}

However, because references have to be set from the beginning, I can never get this to work and have to use a pointer (which I really dislike).

The above doesn't compile.

error: no matching constructor for initialization of 'S'

Is it possible to pass the reference in via the std::array's constructor and avoid using a pointer?


Solution

  • Is it possible to pass the reference in via the std::array's constructor and avoid using a pointer?

    [...] keep repeating c, how to fill out all 1000 elements is the elegant solution?

    If you do not like the pointers, I would suggest using the std::reference_wrapper instead of plane references. Why:

    #include <functional> // std::reference_wrapper
    
    struct S
    {
        constexpr explicit S(C& c) 
            : _c{c} {}
    
    private:
        std::reference_wrapper<C> _c;
    };
    

    Now, to fill the array, (in C++20) you could write a helper make_ref_array() as follows:

    template <size_t N>
    constexpr auto make_ref_array(C& refArg)
    {
        return [&refArg]<size_t... Is>(std::index_sequence<Is...>) {
            return std::array<S, sizeof...(Is)>{(static_cast<void>(Is), S{ refArg })...};
        }(std::make_index_sequence<N>{});
    }
    

    so that, the array creation be like

    C c;
    auto arr = make_ref_array<100u>(c);
    

    See a live demo

    Now, it can be more generic as:

    template <typename T, size_t N, typename... Args>
    constexpr auto make_ref_array(Args&... args)
    {
        return []<size_t... Is, typename... Ts>(std::index_sequence<Is...>, Ts&... ts) {
            return std::array<T, sizeof...(Is)>{(static_cast<void>(Is), T{ ts... })...};
        }(std::make_index_sequence<N>{}, args...);
    }
    

    Note that, all the args will be referenced to construct the T, even if the other data members are not references.


    For the compilers older than C++20, the template lambda must be moved to a separate template function as like in this example