আমি কি কেবল চালিত ধরণের কোনও ভেক্টরকে তালিকাভুক্ত করতে পারি?


96

যদি আমি আমার জিসিসি 4.7 স্ন্যাপশটের মাধ্যমে নিম্নলিখিত কোডটি পাস করি তবে এটি এসটিকে unique_ptrভেক্টরে অনুলিপি করার চেষ্টা করে ।

#include <vector>
#include <memory>

int main() {
    using move_only = std::unique_ptr<int>;
    std::vector<move_only> v { move_only(), move_only(), move_only() };
}

স্পষ্টতই এটি কাজ করতে পারে না কারণ std::unique_ptrঅনুলিপিযোগ্য নয়:

ত্রুটি: মোছা ফাংশন 'স্টডি :: অনন্য_পটি << _ পি পি, _ডিপি> :: অনন্য_সিপি (কনস্টিটিউড স্ট্যান্ড :: অনন্য_প্রেটার <পিপি, _ডিপি> &) [_Tp = ইন্টি সহ; _ডিপি = স্টিড :: ডিফল্ট_ডিলিট; এসটিডি :: অনন্য_আপনার <_Tp, _ডিপি> = স্টাডি :: অনন্য_পিটার] '

প্রারম্ভিক তালিকা থেকে পয়েন্টারগুলি অনুলিপি করার চেষ্টা করে কি জিসিসি সঠিক?


ভিজ্যুয়াল স্টুডিও এবং বিড়ম্বনার একই আচরণ রয়েছে
জিন-সাইমন ব্রোচু

উত্তর:


47

<initializer_list>১৮.৯ এর সংক্ষিপ্তসারটি এটিকে যুক্তিসঙ্গতভাবে পরিষ্কার করে দেয় যে একটি প্রাথমিককরণ তালিকার উপাদানগুলি সর্বদা কনস্ট-রেফারেন্সের মধ্য দিয়ে যায়। দুর্ভাগ্যক্রমে, ভাষাটির বর্তমান সংশোধনীতে প্রাথমিককরণ তালিকার উপাদানগুলিতে মুভ-সিমান্টিক ব্যবহারের কোনও উপায় নেই বলে মনে হয়।

বিশেষত, আমাদের রয়েছে:

typedef const E& reference;
typedef const E& const_reference;

typedef const E* iterator;
typedef const E* const_iterator;

const E* begin() const noexcept; // first element
const E* end() const noexcept; // one past the last element

4
সিপিটিউথগুলি ( cpptruths.blogspot.com/2013/09/… ) এ বর্ণিত <T> আইডিয়ামটি বিবেচনা করুন । ধারণাটি রান-টাইমে লভ্যালু / মূল্য নির্ধারণ করা এবং তারপরে কল মুভ বা অনুলিপি-নির্ধারণ করা। <T> এ প্রাথমিক / তালিকাভুক্ত দ্বারা সরবরাহিত মানক ইন্টারফেসটি কনস্ট রেফারেন্স সত্ত্বেও মূল্য / লভ্যালু সনাক্ত করবে।
সুমন্ত

4
@ সুমান্ট এতটা "আমার কাছে বুদ্ধিমান" বলে মনে হচ্ছে না: এটি কি পরিবর্তে খাঁটি ইউবি নয়? কেবল পুনরুক্তিকারী হিসাবে নয় বরং অন্তর্নিহিত উপাদানগুলি সেগুলি হতে পারে constযা একটি সুগঠিত প্রোগ্রামে ফেলে দেওয়া যায় না।
আন্ডারস্কোর_ডি

66

সম্পাদনা: যেহেতু @ জোহানেস উত্তর হিসাবে সর্বোত্তম সমাধান পোস্ট করতে চায় না বলে আমি কেবল এটি করব।

#include <iterator>
#include <vector>
#include <memory>

int main(){
  using move_only = std::unique_ptr<int>;
  move_only init[] = { move_only(), move_only(), move_only() };
  std::vector<move_only> v{std::make_move_iterator(std::begin(init)),
      std::make_move_iterator(std::end(init))};
}

প্রত্যাবর্তনকৃত পুনরুক্তিগুলি যখন বিন্যস্ত করা std::make_move_iteratorহবে তখন পয়েন্ট-টু এলিমেন্টে স্থানান্তরিত হবে।


আসল উত্তর: আমরা এখানে একটু সাহায্যকারী প্রকারটি ব্যবহার করব:

#include <utility>
#include <type_traits>

template<class T>
struct rref_wrapper
{ // CAUTION - very volatile, use with care
  explicit rref_wrapper(T&& v)
    : _val(std::move(v)) {}

  explicit operator T() const{
    return T{ std::move(_val) };
  }

private:
  T&& _val;
};

// only usable on temporaries
template<class T>
typename std::enable_if<
  !std::is_lvalue_reference<T>::value,
  rref_wrapper<T>
>::type rref(T&& v){
  return rref_wrapper<T>(std::move(v));
}

// lvalue reference can go away
template<class T>
void rref(T&) = delete;

দুঃখের বিষয়, এখানে সোজা-ফরওয়ার্ড কোড কাজ করবে না:

std::vector<move_only> v{ rref(move_only()), rref(move_only()), rref(move_only()) };

যেহেতু স্ট্যান্ডার্ড, যে কোনও কারণেই, রূপান্তরকারী অনুলিপি নির্মাণকারীর সংজ্ঞা দেয় না:

// in class initializer_list
template<class U>
initializer_list(initializer_list<U> const& other);

initializer_list<rref_wrapper<move_only>>বক্রবন্ধনী-init-তালিকা (দ্বারা নির্মিত {...}) রূপান্তর করা হবে না initializer_list<move_only>যে vector<move_only>সময় লাগে। সুতরাং আমাদের এখানে একটি দুই-পদক্ষেপ সূচনা প্রয়োজন:

std::initializer_list<rref_wrapper<move_only>> il{ rref(move_only()),
                                                   rref(move_only()),
                                                   rref(move_only()) };
std::vector<move_only> v(il.begin(), il.end());

4
আহ ... এটাই কি নীতিমালার এনালগ std::ref? এটি বলা উচিত std::rref
কেরেক এসবি

19
এখন, আমি অনুমান করি যে এটি কোনও মন্তব্যে উল্লেখ না করে ছেড়ে দেওয়া উচিত নয় :) move_only m[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v(std::make_move_iterator(m), std::make_move_iterator(m + 3));
জোহানেস স্কাউব -

4
@ জোহানেস: কখনও কখনও, এটি সহজ সমাধান যা আমাকে কেবল সরিয়ে দেয়। যদিও আমি স্বীকার করব, আমি move_iteratorএখনও সেগুলি নিয়ে বিরক্ত করিনি।
Xoo

4
@ জোহানেস: এছাড়াও, কেন এটি উত্তর নয়? :)
শিও

4
@ জোহানলুন্ডবার্গ: আমি এটি একটি QoI ইস্যু বিবেচনা করব, তবে কেন তা করতে পারছে না তা আমি দেখতে পাচ্ছি না। পুনরুক্তি বিভাগের উপর ভিত্তি করে ট্যাগ-প্রেরণের জন্য ভিসি ++ এর স্টিডলিব এবং std::distanceফরোয়ার্ড-বা আরও ভাল পুনরাবৃত্তকারীগুলির জন্য ব্যবহার করে এবং std::move_iteratorঅন্তর্নিহিত পুনরুক্তকারীর বিভাগটি গ্রহণ করে। যাইহোক, ভাল এবং সংক্ষিপ্ত সমাধান। উত্তর হিসাবে পোস্ট করুন, সম্ভবত?
Xoo

10

অন্যান্য উত্তরে উল্লিখিত হিসাবে, আচরণটি std::initializer_listহ'ল মান অনুসারে অবজেক্টগুলি ধরে রাখা এবং বাইরে বেরিয়ে আসা না দেওয়া, সুতরাং এটি সম্ভব নয়। এখানে একটি ফাংশন কল ব্যবহার করে একটি সম্ভাব্য কর্মসংস্থান রয়েছে যেখানে প্রারম্ভিকদের বিভিন্ন ধরণের আর্গুমেন্ট হিসাবে দেওয়া হয়:

#include <vector>
#include <memory>

struct Foo
{
    std::unique_ptr<int> u;
    int x;
    Foo(int x = 0): x(x) {}
};

template<typename V>        // recursion-ender
void multi_emplace(std::vector<V> &vec) {}

template<typename V, typename T1, typename... Types>
void multi_emplace(std::vector<V> &vec, T1&& t1, Types&&... args)
{
    vec.emplace_back( std::move(t1) );
    multi_emplace(vec, args...);
}

int main()
{
    std::vector<Foo> foos;
    multi_emplace(foos, 1, 2, 3, 4, 5);
    multi_emplace(foos, Foo{}, Foo{});
}

দুর্ভাগ্যক্রমে multi_emplace(foos, {});ব্যর্থ হওয়ায় এটি টাইপটি হ্রাস করতে পারে না {}, সুতরাং অবজেক্টগুলি ডিফল্ট-নির্মিত হতে আপনাকে শ্রেণীর নামটি পুনরাবৃত্তি করতে হবে। (বা ব্যবহার vector::resize)


4
পুনরাবৃত্ত প্যাক সম্প্রসারণটি ডামি অ্যারে কমা অপারেটর হ্যাক দ্বারা প্রতিস্থাপন করা যেতে পারে, কোডের কয়েকটি লাইন সংরক্ষণ করতে
এমএম

0

ইয়োহানেস Schaub এর কৌতুক ব্যবহার std::make_move_iterator()সঙ্গে std::experimental::make_array(), আপনি একটি সাহায্যকারী ফাংশন ব্যবহার করতে পারেন:

#include <memory>
#include <type_traits>
#include <vector>
#include <experimental/array>

struct X {};

template<class T, std::size_t N>
auto make_vector( std::array<T,N>&& a )
    -> std::vector<T>
{
    return { std::make_move_iterator(std::begin(a)), std::make_move_iterator(std::end(a)) };
}

template<class... T>
auto make_vector( T&& ... t )
    -> std::vector<typename std::common_type<T...>::type>
{
    return make_vector( std::experimental::make_array( std::forward<T>(t)... ) );
}

int main()
{
    using UX = std::unique_ptr<X>;
    const auto a  = std::experimental::make_array( UX{}, UX{}, UX{} ); // Ok
    const auto v0 = make_vector( UX{}, UX{}, UX{} );                   // Ok
    //const auto v1 = std::vector< UX >{ UX{}, UX{}, UX{} };           // !! Error !!
}

এটি লাইভ দেখুন Coliru

সম্ভবত কেউ সরাসরি তার কাজটি std::make_array()করার অনুমতি make_vector()দেওয়ার কৌশল অবলম্বন করতে পারে তবে আমি কীভাবে দেখিনি (আরও সঠিকভাবে, আমি যা চেষ্টা করি তার চেষ্টা করা উচিত, ব্যর্থ হয়েছিল এবং এগিয়ে গেলাম)। যে কোনও ক্ষেত্রে, সংকলকটি অ্যারেটিকে ভেক্টর ট্রান্সফরমেশনে ইনলাইন করতে সক্ষম হওয়া উচিত, যেমন ক্লাং ও 2 চালু করে GodBolt


-1

এটি চিহ্নিত করা হয়েছে হিসাবে, কেবলমাত্র একটি প্রাথমিক পদক্ষেপের ভেক্টরকে আরম্ভকারী তালিকার সাহায্যে আরম্ভ করা সম্ভব নয়। @ জোহানেস দ্বারা প্রস্তাবিত সমাধানটি সূক্ষ্মভাবে কাজ করে, তবে আমার আর একটি ধারণা আছে ... যদি আমরা কোনও অস্থায়ী অ্যারে তৈরি না করে এবং সেখান থেকে উপাদানগুলিকে ভেক্টরে সরিয়ে না newরাখি , তবে ইতিমধ্যে স্থানে ইতিমধ্যে এই অ্যারেটিকে আরম্ভ করার জন্য স্থাপন ব্যবহার করুন ভেক্টরের স্মৃতি ব্লক?

unique_ptrআর্গুমেন্ট প্যাকটি ব্যবহার করে একটি ভেক্টরকে আরম্ভ করার জন্য এখানে আমার ফাংশনটি রয়েছে :

#include <iostream>
#include <vector>
#include <make_unique.h>  /// @see http://stackoverflow.com/questions/7038357/make-unique-and-perfect-forwarding

template <typename T, typename... Items>
inline std::vector<std::unique_ptr<T>> make_vector_of_unique(Items&&... items) {
    typedef std::unique_ptr<T> value_type;

    // Allocate memory for all items
    std::vector<value_type> result(sizeof...(Items));

    // Initialize the array in place of allocated memory
    new (result.data()) value_type[sizeof...(Items)] {
        make_unique<typename std::remove_reference<Items>::type>(std::forward<Items>(items))...
    };
    return result;
}

int main(int, char**)
{
    auto testVector = make_vector_of_unique<int>(1,2,3);
    for (auto const &item : testVector) {
        std::cout << *item << std::endl;
    }
}

এটি একটি ভয়ানক ধারণা। নতুন স্থাপনা কোনও হাতুড়ি নয়, এটি সূক্ষ্ম নির্ভুলতার একটি সরঞ্জাম। result.data()কিছু এলোমেলো স্মৃতি জন্য পয়েন্টার নয়। এটি কোনও বস্তুর পয়েন্টার । আপনি যখন এটির উপরে নতুন স্থাপন করবেন তখন সেই দরিদ্র অবজেক্টটির কী হবে তা ভেবে দেখুন।
আর মার্টিনহো ফার্নান্দেস

অতিরিক্তভাবে, নতুন স্থান নির্ধারণের অ্যারে ফর্মটি সত্যই ব্যবহারযোগ্য নয় ack
.

@ আর। মার্টিনহো ফার্নান্দেস: অ্যারেগুলির জন্য নতুন স্থান নির্ধারণের কাজটি করবে না বলে উল্লেখ করার জন্য ধন্যবাদ। এখন আমি দেখতে পাচ্ছি কেন এটি একটি খারাপ ধারণা ছিল।
গার্ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.