Why can't I initialize an array of objects if they have private copy constructors?

前端 未结 4 375
夕颜
夕颜 2021-01-24 08:34

I just ran across some unexpected and frustrating behaviour while working on a C++ project. My actual code is a tad more complicated, but the following example captures it just

4条回答
  •  醉梦人生
    2021-01-24 08:54

    Tried just creating the array with a size? Default ctor should be called.

    As in this source

    struct foo {
       int x;
       foo():x(1) {}
    private:
       foo( foo const& ) {}
    };
    
    foo array[10];
    
    #include 
    int main() {
       for (auto&& i:array) {
          std::cout << i.x << "\n";
       }
    }
    

    which demonstrates initialized foo in an array with no default copy constructor.

    If your problem is that you actually want to construct the foo with a non-default constructor, this can be done as well, but it is much harder, and that isn't what your question asked. In any case, here is a very, very rough sketch of the kind of stuff needed to create an array-like structure that supports emplaced construction of its elements. It is far from finished or compiling, but the basic technique should be sound:

    #include 
    #include 
    #include 
    
    
    template
    struct types {};
    
    template
    struct emplacer;
    
    template
    struct remove_refref {
      typedef T type;
    };
    template
    struct remove_refref {
      typedef T type; 
    };
    
    template
    struct emplacer< types, void>:
      emplacer< types >
    {
      typename remove_refref::type val;
      emplacer( A1 arg, Args... args ):
        emplacer< types, index+1 >( std::forward(args)... ),
        val( std::forward(arg) )
      {}
    };
    
    template< std::size_t n >
    struct extract {
      template< typename A1, typename... Args >
      A1&& from( emplacer&& e ) {
        return extract::from( emplacer>&&(e) );
      }
    };
    template<>
    struct extract<0> {
      template< typename A1, typename... Args >
      A1&& from( emplacer&& e ) {
        return std::move( e.val );
      }
    };
    
    template
    struct seq {};
    template
    struct make_seq: make_seq {};
    template
    struct make_seq<0, tail...> {
      typedef seq type;
      type val() { return type(); }
    };
    struct nothing {};
    template
    nothing construct( T* src, emplacer>&& e, seq s = make_seq< sizeof...(Args) >::val() ) {
      new(src)T( std::move( extract( std::move(e) ))... );
    }
    
    template
    emplacer< types > emplace( Args&&... a ) {
      return emplacer< types >( std::forward(a)... );
    }
    
    template
    struct my_array {
    private:
      union T_mem {
        T t;
        char x;
        T_mem():x(0) {}
      };
      T_mem buff[n];
      template
      void do_nothing( nothings...&& ) {}
      template
      my_array( emplacers&&... em, seq s=make_seq< sizeof...(emplacers) >::val() ) {
        do_nothing( construct( &buff[indexes].t, em)... );
      }
      ~my_array() {
        for( auto&& v:buff) {
          v.t.~T();
        }
      }
      T& operator[](std::size_t n) { return buff[n].t; }
      // etc
    };
    

    The idea is that we create an array like construct that is actually an array of union to both T and a char. We thus avoid actually constructing our T, while still having proper alignment and such for one. Assuming char has no non-trivial alignment, the resulting buffer is binary-compatible with a T[].

    We then take as a construction argument emplacer objects, which act as packages for arbitrary construction arguments. For annoying reasons, these objects need to create a temporary copy of some of their parameters (I cannot figure out how to avoid the lifetime issues... maybe I'm missing something).

    The constructor of the my_array takes any number of emplacers and proceeds to construct the contents of buff based on their arguments.

    You'd create your array something like this:

    my_array< Foo, 10 > arr = {
      emplacer( a, b, c ),
      emplacer( x, y, z ),
      ...
    };
    

    a bit more work would allow default construction of uninitialized objects in the array.

    But this is really, really tricky to write correctly.

提交回复
热议问题