দুটি শূন্য-আরগ কনস্ট্রাক্টরকে আলাদা করার আইডোমেটিক উপায়


41

আমার এই জাতীয় ক্লাস রয়েছে:

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}

    // more stuff

};

সাধারণত আমি countsপ্রদর্শিত হিসাবে অ্যারে ডিফল্ট (শূন্য) করতে চাই।

প্রোফাইলিং দ্বারা চিহ্নিত নির্বাচিত স্থানে যাইহোক, আমি অ্যারের সূচনাটি দমন করতে চাই, কারণ আমি জানি অ্যারে ওভাররাইট করা প্রায় শেষ, তবে সংকলকটি এটি বের করার মতো স্মার্ট নয়।

এই জাতীয় "গৌণ" জিরো-আরগ কনস্ট্রাক্টর তৈরির একটি অদ্ভুত এবং দক্ষ উপায় কী?

বর্তমানে, আমি একটি ট্যাগ ক্লাস ব্যবহার করছি uninit_tagযা একটি ডামি আর্গুমেন্ট হিসাবে পাস করা হয়:

struct uninit_tag{};

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}

    event_counts(uninit_tag) {}

    // more stuff

};

তারপরে আমি নো-থ্রি কনস্ট্রাক্টরকে কল করি event_counts c(uninit_tag{});যখন আমি নির্মাণটি দমন করতে চাই।

আমি এমন সমাধানগুলিতে উন্মুক্ত যা কোনও ডামি ক্লাস তৈরির সাথে জড়িত নয় বা কোনওভাবে আরও দক্ষ ইত্যাদি etc.


"কারণ আমি জানি অ্যারে ওভাররাইট করা হবে" আপনি কি 100% নিশ্চিত যে আপনার কম্পাইলার ইতিমধ্যে আপনার জন্য সেই অপ্টিমাইজেশনটি করছে না? কেস পয়েন্ট: gcc.godbolt.org/z/bJnAuJ
ফ্রাঙ্ক

6
@ ফ্র্যাঙ্ক - আমি মনে করি আপনার প্রশ্নের উত্তরটি আপনার উদ্ধৃত বাক্যটির দ্বিতীয়ার্ধে? এটি প্রশ্নের অন্তর্ভুক্ত নয়, তবে বিভিন্ন জিনিস ঘটতে পারে: (ক) প্রায়শই সংকলক মৃত স্টোরগুলি নির্মূল করতে যথেষ্ট শক্তিশালী হয় না (খ) কখনও কখনও কেবলমাত্র উপাদানগুলির একটি উপসেট ওভাররাইট করা হয় এবং এটি পরাজিত করে অপ্টিমাইজেশান (তবে কেবল একই উপসেটটি পরে পড়া হয়) (গ) কখনও কখনও সংকলক এটি করতে পারে তবে পরাজিত হয় যেমন, পদ্ধতিটি ইনলাইনড নয়।
BeeOnRope

আপনার ক্লাসে অন্য কোনও কনস্ট্রাক্টর রয়েছে?
নাথান অলিভার

1
@ ফ্র্যাঙ্ক - হ্যাঁ, আপনার কেস পয়েন্টে দেখায় যে জিসিসি ডেড স্টোরগুলি সরিয়ে দেয় না ? প্রকৃতপক্ষে, আপনি যদি আমাকে অনুমান করিয়েছিলেন আমি ভেবেছিলাম যে জিসিসি এই খুব সাধারণ কেসটি সঠিকভাবে পেয়েছে তবে এখানে যদি এটি ব্যর্থ হয় তবে আরও কিছু জটিল মামলার কল্পনা করুন!
BeeOnRope

1
@uneven_mark - হ্যাঁ, gcc 9.2 এটি -O3 এ করে (তবে এই অপটিমাইজেশনটি -O2, IME এর তুলনায় অস্বাভাবিক), তবে আগের সংস্করণগুলি তা করেনি। সাধারণভাবে ডেড স্টোর নির্মূলকরণ একটি জিনিস, তবে এটি খুব ভঙ্গুর এবং সাধারণ সমস্ত ক্যাভেটের সাপেক্ষে, যেমন সংকলক একই সাথে ডেড স্টোরগুলি দেখতে সক্ষম হতে পারে এটি প্রভাবশালী স্টোরগুলি দেখতে পারে sees আমার মন্তব্য স্পষ্ট করে বলতে চেয়েছিল যে ফ্র্যাঙ্ক কী বলতে চেয়েছিলেন কারণ তিনি "কেস ইন দ্য পয়েন্ট: (গডবোল্ট লিংক)" বলেছিলেন তবে লিঙ্কটি উভয় স্টোরই সম্পাদন করে দেখায় (তাই সম্ভবত আমি কিছু মিস করছি)।
BeeOnRope

উত্তর:


33

আপনার ইতিমধ্যে সমাধানটি সঠিক এবং আমি আপনার কোডটি পর্যালোচনা করছি কিনা তা দেখতে আমি ঠিক দেখতে চাই। এটি যতটা সম্ভব দক্ষ, স্পষ্ট এবং সংক্ষিপ্ত।


1
আমার কাছে প্রধান সমস্যাটি হ'ল আমি uninit_tagএই প্রতিমাটি ব্যবহার করতে চাই এমন প্রতিটি স্থানে আমার নতুন গন্ধের ঘোষণা করা উচিত কিনা । আমি আশা করছিলাম যে ইতিমধ্যে সম্ভবত একটি সূচক টাইপের মতো কিছু ছিল std::
BeeOnRope

9
মানক গ্রন্থাগার থেকে সুস্পষ্ট পছন্দ নেই। আমি যেখানে এই বৈশিষ্ট্যটি চাই সেখানে প্রতি ক্লাসের জন্য আমি একটি নতুন ট্যাগ সংজ্ঞায়িত করব না - আমি একটি প্রকল্প-ব্যাপী no_initট্যাগ সংজ্ঞায়িত করব এবং এটি আমার প্রয়োজনীয় সমস্ত ক্লাসে ব্যবহার করব।
জন জুইনক

2
আমি মনে করি স্ট্যান্ডার্ড লাইব্রেরিতে পুনরাবৃত্তকারী এবং এই জাতীয় স্টাফ এবং দুটি std::piecewise_construct_tএবং আলাদা করার জন্য ম্যানলি ট্যাগ রয়েছে std::in_place_t। এগুলির কোনওটিই এখানে ব্যবহার করা যুক্তিসঙ্গত বলে মনে হয় না। হতে পারে আপনি সর্বদা ব্যবহারের জন্য আপনার ধরণের একটি গ্লোবাল অবজেক্টটি সংজ্ঞায়িত করতে চান, সুতরাং প্রতিটি কনস্ট্রাক্টর কলটিতে আপনার ধনুর্বন্ধনী প্রয়োজন নেই। এসটিএল এর std::piecewise_constructজন্য এটি করে std::piecewise_construct_t
n314159

এটি যতটা সম্ভব দক্ষ না। উদাহরণস্বরূপ AArch64 কলিং কনভেনশনে ট্যাগ, স্ট্যাক-বরাদ্দ করা হয়েছে সঙ্গে কোপ অন প্রভাব (can না পুচ্ছ-কল পারেন ...): godbolt.org/z/6mSsmq
TLW

1
@ টিএলডাব্লু একবার আপনি কন্সট্রাক্টরদের সাথে বডি যুক্ত করলে কোনও স্ট্যাক বরাদ্দ নেই, Godbolt.org/z/vkCD65
R2RT

8

যদি কনস্ট্রাক্টর বডি খালি থাকে তবে এটি বাদ দেওয়া বা ডিফল্ট করা যেতে পারে:

struct event_counts {
    std::uint64_t counts[MAX_COUNTERS];
    event_counts() = default;
};

তারপরে ডিফল্ট সূচনাটি আনইনটিয়ালাইজড event_counts counts;ছেড়ে যাবে counts.counts(ডিফল্ট ইনিশিয়ালাইজেশনটি এখানে কোনও অপশন নেই), এবং মান সূচনাটি আরম্ভকরণের event_counts counts{}; মান দেবে counts.counts, কার্যকরভাবে এটি জিরো দিয়ে পূরণ করবে।


3
তবে তারপরে আবার আপনাকে মান সূচনাটি ব্যবহার করতে হবে এবং ওপি এটি ডিফল্টরূপে নিরাপদ রাখতে চায়।
ডক

@ ডক, আমি সম্মত ওপি যা চায় তার সঠিক সমাধান এটি নয়। তবে এই সূচনাটি অন্তর্নির্মিত ধরণের নকল করে। জন্য int i;এটি শূন্য সক্রিয়া করা হয় না আমরা গ্রহণ। হয়তো আমাদেরও তা গ্রহণ করা উচিত যা event_counts counts;শূন্য-আরম্ভ নয় এবং event_counts counts{};আমাদের নতুন ডিফল্ট করা উচিত।
এভগ

6

আমি আপনার সমাধান পছন্দ। আপনি নেস্টেড স্ট্রাক্ট এবং স্ট্যাটিক ভেরিয়েবল বিবেচনাও করতে পারেন। উদাহরণ স্বরূপ:

struct event_counts {
    static constexpr struct uninit_tag {} uninit = uninit_tag();

    uint64_t counts[MAX_COUNTS];

    event_counts() : counts{} {}

    explicit event_counts(uninit_tag) {}

    // more stuff

};

স্ট্যাটিক ভেরিয়েবল অবিচ্ছিন্ন কন্সট্রাক্টর কলটি আরও সুবিধাজনক বলে মনে হতে পারে:

event_counts e(event_counts::uninit);

আপনি টাইপিং সংরক্ষণ করার জন্য অবশ্যই ম্যাক্রো প্রবর্তন করতে পারেন এবং এটি আরও নিয়মিত বৈশিষ্ট্যযুক্ত করে তুলতে পারেন

#define UNINIT_TAG static constexpr struct uninit_tag {} uninit = uninit_tag();

struct event_counts {
    UNINIT_TAG
}

struct other_counts {
    UNINIT_TAG
}

3

আমি মনে করি একটি ট্যাগ ক্লাস বা বুলের চেয়ে এনাম একটি ভাল পছন্দ। আপনার কোনও কাঠামোর উদাহরণ পাস করার দরকার নেই এবং আপনি যে বিকল্পটি পাচ্ছেন তা কলারের কাছ থেকে এটি পরিষ্কার।

struct event_counts {
    enum Init { INIT, NO_INIT };
    uint64_t counts[MAX_COUNTERS];

    event_counts(Init init = INIT) {
        if (init == INIT) {
            std::fill(counts, counts + MAX_COUNTERS, 0);
        }
    }
};

তারপরে উদাহরণগুলি তৈরি করা দেখতে এরকম দেখাচ্ছে:

event_counts e1{};
event_counts e2{event_counts::INIT};
event_counts e3{event_counts::NO_INIT};

অথবা, ট্যাগ শ্রেণীর পদ্ধতির মতো করে তুলতে, ট্যাগ শ্রেণীর পরিবর্তে একটি একক-মান এনাম ব্যবহার করুন:

struct event_counts {
    enum NoInit { NO_INIT };
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}
    explicit event_counts(NoInit) {}
};

তারপরে উদাহরণ তৈরি করার জন্য কেবল দুটি উপায় রয়েছে:

event_counts e1{};
event_counts e2{event_counts::NO_INIT};

আমি আপনার সাথে একমত: এনাম সহজ। তবে আপনি এই লাইনটি ভুলে গেছেন:event_counts() : counts{} {}
ব্লু করুন

@ ব্লুশ, আমার উদ্দেশ্য countsনিঃশর্তভাবে শুরু করার নয় , কেবল তখনই INITসেট করা হবে।
টিমকে

@ ব্লুশ, আমি মনে করি একটি ট্যাগ ক্লাস বেছে নেওয়ার মূল কারণটি সরলতা অর্জন নয়, তবে ইঙ্গিত দেওয়া যে অবিচ্ছিন্ন বস্তুটি বিশেষ, অর্থাৎ এটি শ্রেণি ইন্টারফেসের স্বাভাবিক অংশের চেয়ে অপ্টিমাইজেশান বৈশিষ্ট্যটি ব্যবহার করে। উভয় boolএবং enumশালীন, তবে আমাদের সচেতন হতে হবে যে ওভারলোডের পরিবর্তে প্যারামিটার ব্যবহার করার ক্ষেত্রে কিছুটা আলাদা শব্দার্থিক ছায়া রয়েছে। পূর্বের একটিতে আপনি স্পষ্টভাবে কোনও বস্তুকে প্যারাম্যাট্রাইজ করেন, তাই আঞ্চলিকভাবে / অবিশ্বাস্য অবস্থানটি তার রাজ্যে পরিণত হয়, অন্যদিকে কোনও টেস্ট অবজেক্টকে সিটারে পাস করা ক্লাসকে রূপান্তর করতে বলার মতো। সুতরাং এটি আইএমও সিন্টেক্সটিক্যাল পছন্দের বিষয় নয়।
ডক

@ টিমক তবে ওপি ডিফল্ট আচরণটি অ্যারের সূচনা হতে চায় তাই আমি মনে করি আপনার প্রশ্নের সমাধানটি অন্তর্ভুক্ত করা উচিত event_counts() : counts{} {}
নীলাভ

@ ব্লুশ আমার মূল পরামর্শটি অনুরোধ countsকরা std::fillনা হলে শুরু করা NO_INITহয়। আপনার পরামর্শ অনুসারে ডিফল্ট কনস্ট্রাক্টর যুক্ত করা ডিফল্ট সূচনা করার দুটি ভিন্ন উপায় তৈরি করবে, এটি দুর্দান্ত ধারণা নয়। আমি অন্য পদ্ধতির যোগ করেছি যা ব্যবহার এড়ানো যায় না std::fill
টিমকে

1

আপনি আপনার শ্রেণীর জন্য একটি দ্বি-পর্যায়ে সূচনা বিবেচনা করতে চাইতে পারেন :

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() = default;

    void set_zero() {
       std::fill(std::begin(counts), std::end(counts), 0u);
    }
};

উপরের কনস্ট্রাক্টর অ্যারের শূন্য থেকে আরম্ভ করে না। অ্যারের উপাদানগুলিকে শূন্যতে সেট করতে, আপনাকে set_zero()নির্মাণের পরে সদস্য ফাংশনটি কল করতে হবে ।


7
ধন্যবাদ, আমি এই পদ্ধতির বিষয়টি বিবেচনা করেছি তবে এমন কিছু চাই যা ডিফল্টটিকে নিরাপদ রাখে - অর্থাত্ ডিফল্টরূপে শূন্য এবং কেবলমাত্র কয়েকটি নির্বাচিত স্থানে আমি আচরণটি অনিরাপদকে ওভাররাইড করি।
BeeOnRope

3
এটি অনির্বাচিত হওয়ার কথা বলার অপেক্ষা রাখে না তবে অতিরিক্ত যত্ন নেওয়া প্রয়োজন । সুতরাং এটি ওপিএস সমাধানের তুলনায় বাগের অতিরিক্ত উত্স।
আখরোট

@BeOnRope কে ডিফল্ট আর্গুমেন্টের std::functionঅনুরূপ কিছু দিয়ে কনস্ট্রাক্টর আর্গুমেন্ট হিসাবে সরবরাহ করতে পারে set_zero। আপনি যদি অবিচ্ছিন্ন অ্যারে চান তবে আপনি একটি ল্যাম্বডা ফাংশনটি পাস করবেন।
ডক

1

আমি এটি এইভাবে করব:

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}

    event_counts(bool initCounts) {
        if (initCounts) {
            std::fill(counts, counts + MAX_COUNTERS, 0);
        }
    }
};

সংকলকটি আপনি যখন ব্যবহার করবেন তখন সমস্ত কোড এড়িয়ে যেতে যথেষ্ট স্মার্ট হবে event_counts(false)এবং আপনার শ্রেণীর ইন্টারফেসটিকে এত অদ্ভুত করার পরিবর্তে আপনি কী বোঝাতে চেয়েছেন তা সঠিকভাবে বলতে পারেন।


8
আপনি দক্ষতার বিষয়ে সঠিক, তবে বুলিয়ান প্যারামিটারগুলি পঠনযোগ্য ক্লায়েন্ট কোডের জন্য তৈরি করে না। আপনি যখন পাশাপাশি পড়ছেন, এবং আপনি ঘোষণাটি দেখেন event_counts(false), তার অর্থ কী? ফিরে না গিয়ে প্যারামিটারটির নাম না দেখে আপনার কোনও ধারণা নেই । কমপক্ষে একটি এনাম ব্যবহার করা ভাল, বা এই ক্ষেত্রে, প্রশ্নের মধ্যে যেমন দেখানো হয়েছে তেমন একটি সেন্ডিনেল / ট্যাগ ক্লাস। তারপরে আপনি আরও একটি মত বিবৃতি পাবেন event_counts(no_init)যা এর অর্থ সকলের কাছে সুস্পষ্ট।
কোডি গ্রে

আমি মনে করি এটিও শালীন সমাধান। আপনি ডিফল্ট ctor বাতিল করতে পারেন এবং ডিফল্ট মান ব্যবহার করতে পারেন event_counts(bool initCountr = true)
ডক

এছাড়াও, ctor স্পষ্ট করা উচিত।
ডক

দুর্ভাগ্যক্রমে বর্তমানে সি ++ নামযুক্ত পরামিতিগুলিকে সমর্থন করে না, তবে আমরা ব্যবহারের জন্য boost::parameterএবং event_counts(initCounts = false)পাঠযোগ্যতার জন্য কল করতে পারি
ফুক্লাভি

1
অদ্ভুতভাবে যথেষ্ট, @doc, event_counts(bool initCounts = true)আসলে হয় একটি ডিফল্ট কন্সট্রাকটর, যে ডিফল্ট মান না থাকার প্যারামিটার কারণে। প্রয়োজনীয়তা কেবলমাত্র এটি যে আর্গুমেন্ট নির্দিষ্ট না করে কল করা যায়, event_counts ec;প্যারামিটারটি কম হয় বা ডিফল্ট মান ব্যবহার করে কিনা তা বিবেচ্য নয়।
জাস্টিন সময় - মনিকা

1

আমি কেবল একটি টাইপিং কিছুটা সংরক্ষণ করতে একটি সাবক্লাস ব্যবহার করব:

struct event_counts {
    uint64_t counts[MAX_COUNTERS];

    event_counts() : counts{} {}
    event_counts(uninit_tag) {}
};    

struct event_counts_no_init: event_counts {
    event_counts_no_init(): event_counts(uninit_tag{}) {}
};

তোমার আরম্ভের না কন্সট্রাকটর আর্গুমেন্ট পরিবর্তন করে ডামি বর্গ পরিত্রাণ পেতে পারেন boolবাint বা কিছু, যেমন এটা আর স্মৃতিসম্বন্ধীয় হতে নেই।

আপনি উত্তরাধিকারের চারপাশে অদলবদল করতে পারেন এবং events_count_no_initএভগের মতো তাদের উত্তরে প্রস্তাবিত একটি ডিফল্ট কন্সট্রাক্টর দিয়ে সংজ্ঞা দিতে পারেন এবং তারপরে events_countসাবক্লাস হতে পারে:

struct event_counts_no_init {
    uint64_t counts[MAX_COUNTERS];
    event_counts_no_init() = default;
};

struct event_counts: event_counts_no_init {
    event_counts(): event_counts_no_init{} {}
};

এটি একটি আকর্ষণীয় ধারণা, তবে আমার মনে হয় যে কোনও নতুন ধরণের প্রবর্তন ঘর্ষণ সৃষ্টি করবে। উদাহরণস্বরূপ, যখন আমি আসলে একটি অবিচ্ছিন্ন করতে চাই event_counts, আমি এটি টাইপ হতে চাই event_count, না event_count_uninitialized, তাই আমার ঠিক যেমন নির্মাণে টুকরো টুকরো করা উচিত event_counts c = event_counts_no_init{};, যা আমার মনে হয় টাইপিংয়ের বেশিরভাগ সঞ্চয় সরিয়ে দেয়।
BeeOnRope

@ বিঅনরোপ ওয়েল, বেশিরভাগ উদ্দেশ্যে কোনও event_count_uninitializedবস্তু একটি event_countবস্তু। এটি উত্তরাধিকারের পুরো বিষয়, তারা সম্পূর্ণ ভিন্ন ধরণের নয়।
রস রিজ

সম্মত, কিন্তু ঘষাটি "বেশিরভাগ উদ্দেশ্যে" রয়েছে। , আপনি যদি বরাদ্দ দেখতে চেষ্টা যেমন - তারা বিনিমেয় হয় না ecuকরতে ecএটি কাজ করে অন্য উপায় কাছাকাছি, কিন্তু। অথবা আপনি যদি টেমপ্লেট ফাংশন ব্যবহার করেন তবে এগুলি বিভিন্ন ধরণের এবং আচরণটি অভিন্ন হওয়ার পরেও বিভিন্ন ইনস্ট্যান্টেশন সহ শেষ হয় (এবং কখনও কখনও এটি উদাহরণস্বরূপ হয় না, স্থির টেম্পলেট সদস্যদের সাথে)। বিশেষত এর অত্যধিক ব্যবহারের সাথে autoঅবশ্যই ফসল উঠতে পারে এবং বিভ্রান্তিকর হতে পারে: আমি কোনও জিনিসকে আরম্ভ করার পদ্ধতিটি স্থায়ীভাবে তার ধরণে প্রতিবিম্বিত হওয়ার জন্য চাই না।
BeeOnRope
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.