সামগ্রিক সূচনা সদস্যদের বাদ দেওয়া কি সম্ভব?


43

আমার মতো একই ধরণের অনেক সদস্যের সাথে একটি কাঠামো রয়েছে

struct VariablePointers {
   VariablePtr active;
   VariablePtr wasactive;
   VariablePtr filename;
};

সমস্যাটি হ'ল যদি আমি স্ট্রাক্ট সদস্যগুলির মধ্যে একটির (যেমন wasactive) এর সূচনা করতে ভুলে যাই তবে :

VariablePointers{activePtr, filename}

সংকলক এটি সম্পর্কে অভিযোগ করবে না, তবে আমার একটি জিনিস থাকবে যা আংশিকভাবে শুরু করা হয়েছে। আমি কীভাবে এই জাতীয় ত্রুটি রোধ করতে পারি? আমি কোনও কনস্ট্রাক্টর যুক্ত করতে পারলাম, তবে এটি ভেরিয়েবলের তালিকাটি দুবার নকল করবে, সুতরাং আমাকে এই তিনটি বার টাইপ করতে হবে!

সি ++ 11 (বর্তমানে আমি সেই সংস্করণে সীমাবদ্ধ রয়েছি) এর কোনও সমাধান থাকলে, দয়া করে সি ++ 11 টি উত্তরও যুক্ত করুন । যদিও সাম্প্রতিকতম ভাষা মানগুলিও স্বাগত, যদিও!


6
কনস্ট্রাক্টর টাইপ করা এত ভয়ানক শোনায় না। যদি আপনার খুব বেশি সদস্য না থাকে তবে এক্ষেত্রে সম্ভবত রিফ্যাক্টরিংয়ের ব্যবস্থা রয়েছে।
গোনেন আই

1
@ সোমোপ্রগ্রামমারডুড আমার মনে হয় তার অর্থ ত্রুটিটি হ'ল আপনি দুর্ঘটনাক্রমে একটি আরম্ভের মানটি বাদ দিতে পারেন
গোনেন আই

2
@ থাইজিজব্রো যদি আপনি জানেন কিভাবে অ্যারে / ভেক্টর আপনাকে একটি উত্তর পোস্ট করতে সাহায্য করে। এটি সুস্পষ্ট নয়, আমি এটি দেখতে চাই না
idclev 463035818

2
@ সোমপ্রগ্রামগ্রামারডুড কিন্তু এটি কি একটি সতর্কবাণী? এটি VS2019 দিয়ে দেখতে পাচ্ছেন না।
acraig5075

8
একটি -Wmissing-field-initializersসংকলন পতাকা আছে।
রন

উত্তর:


42

এখানে একটি কৌশল যা কোনও প্রারম্ভিক ব্যবস্থাপক অনুপস্থিত থাকলে একটি লিঙ্কার ত্রুটি ট্রিগার করে:

struct init_required_t {
    template <class T>
    operator T() const; // Left undefined
} static const init_required;

ব্যবহার:

struct Foo {
    int bar = init_required;
};

int main() {
    Foo f;
}

ফলাফল:

/tmp/ccxwN7Pn.o: In function `Foo::Foo()':
prog.cc:(.text._ZN3FooC2Ev[_ZN3FooC5Ev]+0x12): undefined reference to `init_required_t::operator int<int>() const'
collect2: error: ld returned 1 exit status

আদেশ সহকারে:

  • সি ++ ১৪ এর আগে এটি মোটেও Fooমোটামুটি হতে বাধা দেয় ।
  • এটি প্রযুক্তিগতভাবে অনির্ধারিত আচরণের উপর নির্ভর করে (ওডিআর লঙ্ঘন), তবে কোনও বুদ্ধিমান প্ল্যাটফর্মে কাজ করা উচিত।

আপনি রূপান্তর অপারেটরটি মুছতে পারেন এবং তারপরে এটি একটি সংকলক ত্রুটি।
jrok

@ জ্রোক হ্যাঁ, তবে এটি Fooঘোষণার সাথে সাথেই এটি হ'ল , এমনকি যদি আপনি প্রকৃতপক্ষে কখনও অপারেটরকে কল করেন না।
কোয়ান্টিন

2
@ জ্রোক তবে তারপরেও সূচনা সরবরাহ করা হলেও এটি সংকলন করে না। Godbolt.org/z/yHZNq_ সংযোজন: এমএসভিসির জন্য এটি আপনার বর্ণনার মতো কাজ করে: Godbolt.org/z/uQSvDa এটি কি বাগ?
n314159

অবশ্যই, আমাকে বোকা।
jrok

6
দুর্ভাগ্যক্রমে, এই কৌশলটি সি ++ 11 এর সাথে কাজ করে না, যেহেতু এটি তখন একটি অ-সমষ্টিগত হয়ে উঠবে :( আমি সি ++ 11 ট্যাগটি সরিয়ে দিয়েছি, সুতরাং আপনার উত্তরটি কার্যকর যেমন রয়েছে (দয়া করে এটি মুছবেন না) তবে যদি সম্ভব হয় তবে একটি সি ++ 11 সমাধান এখনও পছন্দসই
জোহানেস স্কাউব - লিটব

22

ঝনঝন এবং জিসিসির জন্য আপনি এটি সঙ্কলন করতে পারেন -Werror=missing-field-initializersযা অনুপস্থিত ক্ষেত্রের প্রারম্ভিকদের উপর সতর্কতাটিকে একটি ত্রুটিতে পরিণত করে। godbolt

সম্পাদনা: এমএসভিসির জন্য, স্তরে এমনকি কোনও সতর্কতা নির্গত হয় বলে মনে হয় না /Wall, তাই আমি মনে করি না যে এই সংকলকটি দিয়ে অনুপস্থিত আরম্ভকারীদের সম্পর্কে সতর্ক করা সম্ভব। godbolt


7

একটি মার্জিত এবং সহজ সমাধান নয়, আমি মনে করি ... তবে সি ++ 11 এর সাথেও কাজ করা উচিত এবং একটি সংকলন-সময় (লিঙ্ক-টাইম নয়) ত্রুটি দেওয়া উচিত।

ধারণাটি হ'ল আপনার স্ট্রাক্টে একটি অতিরিক্ত সদস্যকে যুক্ত করুন, শেষ অবস্থানে, কোনও ধরণের ডিফল্ট সূচনা ছাড়াই (এবং এটি প্রকারের মান দিয়ে শুরু করতে পারে না VariablePtr(বা পূর্ববর্তী মানগুলির প্রকার যা কিছু থাকে)

উদাহরণস্বরূপ

struct bar
 {
   bar () = delete;

   template <typename T> 
   bar (T const &) = delete;

   bar (int) 
    { }
 };

struct foo
 {
   char a;
   char b;
   char c;

   bar sentinel;
 };

আপনি আপনার সামগ্রিক সূচনা তালিকাতে সমস্ত উপাদান যুক্ত করতে বাধ্য হয়েছেন, শেষ মানটি ( sentinelযেমন একটি পূর্ণসংখ্যার উদাহরণস্বরূপ) স্পষ্টভাবে আরম্ভ করার জন্য মানটি অন্তর্ভুক্ত করেছেন বা আপনি "বার" মুছে ফেলা নির্মাণকারীর কল পেয়েছেন "ত্রুটি"।

সুতরাং

foo f1 {'a', 'b', 'c', 1};

সংকলন এবং

foo f2 {'a', 'b'};  // ERROR

না।

দুর্ভাগ্যক্রমেও

foo f3 {'a', 'b', 'c'};  // ERROR

সংকলন করে না

- সম্পাদনা -

MSalters দ্বারা চিহ্নিত (ধন্যবাদ) আমার মূল উদাহরণে একটি ত্রুটি আছে (অন্য ত্রুটি): একটি barমান একটি মান দিয়ে শুরু করা যেতে পারে char(যা রূপান্তরযোগ্য int), সুতরাং নিম্নলিখিত সূচনাটি কাজ করে

foo f4 {'a', 'b', 'c', 'd'};

এবং এটি অত্যন্ত বিভ্রান্তিকর হতে পারে।

এই সমস্যাটি এড়াতে, আমি নিম্নলিখিত মুছে ফেলা টেমপ্লেট নির্মাণকারী যুক্ত করেছি

 template <typename T> 
 bar (T const &) = delete;

সুতরাং পূর্ববর্তী f4ঘোষণাপত্রটি একটি সংকলন ত্রুটি দেয় কারণ dমানটি মুছে ফেলা টেম্পলেট নির্মাণকারীর দ্বারা আটকে থাকে


ধন্যবাদ, এটি দুর্দান্ত! আপনি উল্লিখিত হিসাবে এটি নিখুঁত নয়, এবং foo f;সংকলন করতে ব্যর্থ করে তোলে , তবে এটি সম্ভবত এই কৌশলটির ত্রুটিযুক্ত বৈশিষ্ট্যের চেয়ে আরও বেশি বৈশিষ্ট্যযুক্ত। এর চেয়ে ভাল কোনও প্রস্তাব না থাকলে গ্রহণ করবে।
জোহানেস স্কাউব -

1
আমি বার কনস্ট্রাক্টরকে কনস্টেড নেস্টেড ক্লাসের সদস্যকে গ্রহণযোগ্য করে তুলব যা পাঠযোগ্যতার জন্য init_list_end এর মতো কিছু বলেছিল
Gonen I

@ গনেনআই - পাঠযোগ্যতার জন্য আপনি কোনওটির একটি enumনাম এবং নাম init_list_end(o কেবলমাত্র list_end) গ্রহণ করতে পারেন enum; তবে পঠনযোগ্যতা অনেকগুলি টাইপরাইটিং যুক্ত করে, সুতরাং অতিরিক্ত মান এই উত্তরের দুর্বল পয়েন্ট হওয়ায়, আমি জানি না এটি ভাল ধারণা কিনা।
সর্বোচ্চ 66

হয়তো ভালো কিছু যোগ constexpr static int eol = 0;এর হেডারের মধ্যে bartest{a, b, c, eol}আমার কাছে বেশ পঠনযোগ্য মনে হচ্ছে।
n314159

@ n314159 - ভাল ... হয়ে যান bar::eol; এটি প্রায় একটি enumমূল্য পাস হিসাবে ; তবে আমি এটি গুরুত্বপূর্ণ মনে করি না: উত্তরের মূলটি হ'ল ডিফল্ট ইনিশিয়েশন ছাড়াই আপনার স্ট্রাক্টে একটি অতিরিক্ত সদস্য, শেষ অবস্থানে, একটি অতিরিক্ত সদস্য যুক্ত করুন "; barঅংশ শো সমাধান কাজ করে শুধু একটি তুচ্ছ উদাহরণ; সঠিক "ডিফল্ট সূচনা ব্যতীত" পরিস্থিতি থেকে নির্ভর করা উচিত (আইএমএইচও)।
সর্বোচ্চ 66

4

জন্য CppCoreCheck সেখানে ঠিক চেক করার জন্য একটি নিয়ম যে, সকল সদস্যদের সক্রিয়া করা হয়েছে যদি এবং সতর্কতাটি থেকে একটি ত্রুটি পরিণত হতে পারে - অবশ্যই হয় যা সাধারণত প্রোগ্রাম ব্যাপী।

হালনাগাদ:

আপনি যে নিয়মটি পরীক্ষা করতে চান তা টাইপসফিটের অংশ Type.6:

প্রকার 6: সর্বদা একটি সদস্য পরিবর্তনশীল সূচনা করুন: সর্বদা আরম্ভ করুন, সম্ভবত ডিফল্ট নির্মাণকারী বা ডিফল্ট সদস্য আরম্ভকারী ব্যবহার করুন।


2

সহজ উপায়টি হ'ল সদস্যদের প্রকারটি কোনও নো-আরগ নির্মাতা না দেওয়া:

struct B
{
    B(int x) {}
};
struct A
{
    B a;
    B b;
    B c;
};

int main() {

        // A a1{ 1, 2 }; // will not compile 
        A a1{ 1, 2, 3 }; // will compile 

অন্য বিকল্প: আপনার সদস্যরা যদি কনস্ট্যান্ড হন এবং আপনার সমস্তগুলি শুরু করতে হবে:

struct A {    const int& x;    const int& y;    const int& z; };

int main() {

//A a1{ 1,2 };  // will not compile 
A a2{ 1,2, 3 }; // compiles OK

আপনি যদি একটি ডামি কনস্ট এবং সদস্যের সাথে বেঁচে থাকতে পারেন তবে আপনি এটি একটি মেসিনেল সম্পর্কে @ ম্যাক্স 66 এর ধারণার সাথে একত্রিত করতে পারেন।

struct end_of_init_list {};

struct A {
    int x;
    int y;
    int z;
    const end_of_init_list& dummy;
};

    int main() {

    //A a1{ 1,2 };  // will not compile
    //A a2{ 1,2, 3 }; // will not compile
    A a3{ 1,2, 3,end_of_init_list() }; // will compile

Cppreferences https://en.cppreferences.com/w/cpp/language/aggregate_initialization থেকে

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

আর একটি বিকল্প হ'ল ম্যাক্স 66 এর সেন্ডিনেল ধারণা নেওয়া এবং পাঠযোগ্যতার জন্য কিছু সিনট্যাকটিক চিনি যুক্ত করা

struct init_list_guard
{
    struct ender {

    } static const end;
    init_list_guard() = delete;

    init_list_guard(ender e){ }
};

struct A
{
    char a;
    char b;
    char c;

    init_list_guard guard;
};

int main() {
   // A a1{ 1, 2 }; // will not compile 
   // A a2{ 1, init_list_guard::end }; // will not compile 
   A a3{ 1,2,3,init_list_guard::end }; // compiles OK

দুর্ভাগ্যক্রমে, এটি Aঅস্থাবর করে তোলে এবং অনুলিপি-শব্দার্থবিজ্ঞানের পরিবর্তন করে ( Aকথা বলার জন্য আর কোনও মূল্যবোধের সমষ্টি নয়) :(
জোহানেস শ্যাব - লিটব

@ জোহানেস শ্যাব-লিটব ঠিক আছে। আমার সম্পাদিত উত্তরে এই ধারণাটি সম্পর্কে কীভাবে?
গোনেন আই

@ জোহানেস শ্যাব-লিটব: সমানভাবে গুরুত্বপূর্ণ, প্রথম সংস্করণ সদস্যদের পয়েন্টার করে ইন্ডিয়ারেশনের একটি স্তর যুক্ত করে। আরও গুরুত্বপূর্ণ বিষয়, তাদের কোনও কিছুর জন্য একটি উল্লেখ হতে হবে এবং 1,2,3অবজেক্টগুলি কার্যকরভাবে স্থানীয়ভাবে স্বয়ংক্রিয় স্টোরেজ যা ফাংশন শেষ হওয়ার পরে সুযোগের বাইরে চলে যায়। এবং এটি 64-বিট পয়েন্টার (x86-64 এর মতো) সহ একটি সিস্টেমে 3 এর পরিবর্তে আকার (A) 24 তৈরি করে।
পিটার কর্ডেস

একটি ডামি রেফারেন্স আকারটি 3 থেকে 16 বাইট থেকে বাড়িয়ে দেয় (পয়েন্টার (রেফারেন্স) সদস্যের প্রান্তিককরণের জন্য প্যাডিং + পয়েন্টার নিজেই)) সুযোগ। আমি অবশ্যই এটিকে অপ্টিমাইজ করে না নিয়ে চিন্তিত করব এবং এটিকে অনুলিপি করা অবশ্যই হবে না। (খালি শ্রেণীর আকারের তুলনায় অন্যটি অনুকূল করার আরও ভাল সুযোগ রয়েছে, সুতরাং এখানে তৃতীয় বিকল্পটি সবচেয়ে কম খারাপ তবে এটি এখনও কমপক্ষে কিছু এবিআইতে প্রতিটি বস্তুর জন্য জায়গা ব্যয় করে I'd কিছু ক্ষেত্রে অপ্টিমাইজেশন।)
পিটার কর্ডেস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.