সি ++ তে ওভারলোডেড কনস্ট্রাক্টরগুলির মাধ্যমে অজানা প্রকারের একটি পরিবর্তনশীল সূচনা


22

প্রাথমিকভাবে পাইথনের পটভূমি থেকে আগত আমি সি ++ তে প্রকারের সাথে কাজ করে কিছুটা লড়াই করেছি।

আমি বেশ কয়েকটি ওভারলোডেড কনস্ট্রাক্টরের মধ্যে একটির মাধ্যমে বর্গ ভেরিয়েবল শুরু করার চেষ্টা করছি যা বিভিন্ন ধরণের পরামিতি হিসাবে নেয়। আমি পড়েছি autoকীওয়ার্ডটি ব্যবহার করে একটি ভেরিয়েবলের স্বয়ংক্রিয় ঘোষণার জন্য ব্যবহার করা যেতে পারে, তবে আমার ক্ষেত্রে এটি নির্মাণকারী চয়ন না করা পর্যন্ত এটি আরম্ভ করা হবে না। তবে সংকলক আরম্ভ না করায় সন্তুষ্ট নয় value

class Token {
public:

    auto value;

    Token(int ivalue) {
        value = ivalue;
    }
    Token(float fvalue) {
        value = fvalue;
    }
    Token(std::string svalue) {
        value = svalue;
    }

    void printValue() {
        std::cout << "The token value is: " << value << std::endl;
    }
};

অজগরটিতে এটির মতো দেখতে পাওয়া যেতে পারে:

class Token():
        def __init__(self, value):
             self.value = value

        def printValue(self):
             print("The token value is: %s" % self.value)

autoএই পরিস্থিতিতে কীওয়ার্ডটি ব্যবহারের সঠিক উপায়টি কী ? আমার কি সম্পূর্ণ আলাদা পদ্ধতি ব্যবহার করা উচিত?


2
আমি বিশ্বাস করি আপনি autoক্লাসের সদস্যদের জন্য মোটেই ব্যবহার করতে পারবেন না ? প্রাসঙ্গিক তবে পুরানো প্রশ্ন: একটি "অটো" সদস্যের পরিবর্তনশীল থাকা কি সম্ভব?
ইয়্যাকিসারভিনেন

টেমপ্লেট ব্যবহার না করার কোনও কারণ?
জিমি আরটি

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

উত্তর:


17

সি ++ তে ওভারলোডেড কনস্ট্রাক্টরগুলির মাধ্যমে অজানা প্রকারের একটি পরিবর্তনশীল সূচনা

সি ++ তে "অজানা প্রকারের পরিবর্তনশীল" বলে কোনও জিনিস নেই।

এই পরিস্থিতিতে অটো কীওয়ার্ডটি ব্যবহারের সঠিক উপায় কী?

স্বতঃ-অনুমিত ভেরিয়েবলগুলির একটি প্রকার থাকে যা ইনিশিয়ালার থেকে বাদ দেওয়া হয়। যদি কোনও প্রাথমিককরণকারী না থাকে তবে আপনি অটো ব্যবহার করতে পারবেন না। অ স্থিতিশীল সদস্য ভেরিয়েবলের জন্য অটো ব্যবহার করা যাবে না। শ্রেণীর একটি উদাহরণে অন্য উদাহরণগুলির চেয়ে আলাদাভাবে টাইপ করা সদস্য থাকতে পারে না।

এই দৃশ্যে অটো কীওয়ার্ড ব্যবহার করার কোনও উপায় নেই।

আমার কি সম্পূর্ণ আলাদা পদ্ধতি ব্যবহার করা উচিত?

সম্ভবত। দেখে মনে হচ্ছে আপনি একটি বাস্তবায়ন করার চেষ্টা করছেন std::variant। এক্স সংখ্যার মধ্যে একটির সংরক্ষণের জন্য আপনার যদি ভেরিয়েবলের প্রয়োজন হয় তবে এটি আপনার ব্যবহার করা উচিত।

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


ইউনিয়ন ব্যবহার করাও কি এই ক্ষেত্রে যোগ্যতা অর্জন করবে?
ওয়েজরা

unionএকটি ত্রুটি-প্রবণ নিম্ন-স্তরের প্রক্রিয়া। variantসম্ভবত এটি অভ্যন্তরীণভাবে ব্যবহার করে এবং এর ব্যবহারকে আরও সুরক্ষিত করে।
এরলকোনিগ

@ ওন্ড্রা ইউনিয়ন নিজেই খুব কার্যকর হবে না কারণ বর্তমানে কোন সদস্যের জন্য এটি সক্রিয় রয়েছেন তা পরিদর্শন করা যায় না। এটি স্ট্যান্ড :: স্ট্রিংয়ের মতো অ-তুচ্ছ ক্লাসগুলি (যা কাস্টম ডেস্ট্রাক্টর রয়েছে) দিয়ে ব্যবহার করা খুব বেদনাদায়ক। যা চাইবে তা হ'ল ট্যাগ ইউনিয়ন। স্টেট :: ভেরিয়েন্ট প্রয়োগসমূহ যা ডেটাস্ট্রাকচার।
এরেরিকা

1
আগে থেকে libstdc ++ এর variant আছে ব্যবহার union। বিকল্প, কাঁচা মেমরি এবং নতুন স্থাপনার ব্যবহার করে কোনও constexprকনস্ট্রাক্টরে ব্যবহার করা যাবে না ।
এরলকোনিগ

@ এরলকোনিগ যথেষ্ট ভাল, আমি যা বলেছিলাম তা ফিরিয়ে নিই। আমি কেবল বুস্ট বাস্তবায়নের দিকে লক্ষ্য করেছি, যা ইউনিয়ন ব্যবহার করে না, এবং ধরে নিয়েছে যে সবাই একই রকম করেছে।
এরেরিকা

11

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

আপনি যা খুঁজছেন তা পরিবর্তে সি ++ টেম্পলেট শ্রেণি ব্যবহার করে করা যেতে পারে, যা বিভিন্ন ধরণের শ্রেণীর একাধিক সংস্করণ তৈরি করতে দেয়।

এই কোডটি আপনার সন্ধানের উত্তর হতে পারে।

template <typename T>
class Token {
private:
    T value;

public:
    Token(const T& ivalue) {
        value = ivalue;
    }

    void printValue() {
        std::cout << "The token value is: " << value << std::endl;
    }
};

কিছু শর্ত পূরণ হলে এই কোডটি সংকলন করবে যেমন ফাংশনটি operator<<স্ট্যান্ড :: অস্ট্রিমে & টাইপ করে টি জন্য সংজ্ঞায়িত করা উচিত like


6

অন্যরা যে প্রস্তাব দিয়েছে তার চেয়ে ভিন্ন ভিন্ন দৃষ্টিভঙ্গি হ'ল টেমপ্লেটগুলি ব্যবহার করা। এখানে একটি উদাহরণ:

template<class T>
class Token {
public:

    T value;

    Token(T value) :
        value(std::move(value))
    {}

    void printValue() {
        std::cout << "The token value is: " << value << std::endl;
    }
};

তারপরে আপনি নিজের ক্লাসটি এভাবে ব্যবহার করতে পারেন:

Token<int> x(5);
x.printValue();

3

আপনি std::variantটাইপ ব্যবহার করতে পারেন । নীচের কোডটি একটি উপায় দেখায় (তবে এটি কিছুটা আনাড়ি, আমাকে স্বীকার করতে হবে):

#include <iostream>
#include <variant>

class Token {
public:

    std::variant<int, float, std::string> value;

    Token(int ivalue) {
        value = ivalue;
    }
    Token(float fvalue) {
        value = fvalue;
    }
    Token(std::string svalue) {
        value = svalue;
    }

    void printValue() {
        switch (value.index()) {
            case 0:
                std::cout << "The token value is: " << std::get<0>(value) << std::endl;
                break;
            case 1:
                std::cout << "The token value is: " << std::get<1>(value) << std::endl;
                break;
            case 2:
                std::cout << "The token value is: " << std::get<2>(value) << std::endl;
                break;
        }
    }
};

int main() {
    Token it(1);
    Token ft(2.2f);
    Token st("three");
    it.printValue();
    ft.printValue();
    st.printValue();
    return 0;
}

এটি খুব সুন্দর হবে যদি এটি std::get<0>(value)হিসাবে লেখা যেতে পারে std::get<value.index()>(value)তবে হায়, "এক্স" এর <x>একটি সংকলন-সময় ধ্রুবক প্রকাশ হতে হবে।


1
এর std::visitপরিবর্তে সম্ভবত ব্যবহার করা ভাল switch
এরেরিকা

1

auto একটি নির্দিষ্ট ধরণের জন্য অবশ্যই ছাড়যোগ্য হতে হবে, এটি রানটাইম ডায়নামিক টাইপ সরবরাহ করে না।

যদি ঘোষণার সময় Tokenআপনি সমস্ত সম্ভাব্য প্রকারগুলি জানেন যে আপনি ব্যবহার করতে পারেন std::variant<Type1, Type2, Type3>ইত্যাদি This এটি একটি "টাইপ এনুম" এবং "ইউনিয়ন" থাকার মতো। এটি নিশ্চিত করে যে যথাযথ নির্মাণকারী এবং ধ্বংসকারীদের ডাকা হবে।

std::variant<int, std::string> v;
v = "example";
v.index(); // 1, a int would be 0
std::holds_alternative<std::string>(v); // true
std::holds_alternative<int>(v); // false
std::get<std::string>(v); // "example"
std::get<int>(v); // throws std::bad_variant_access

বিকল্প Tokenহ'ল উপযুক্ত ভার্চুয়াল পদ্ধতিতে প্রতিটি ক্ষেত্রে (সম্ভবত টেমপ্লেট ব্যবহার করে) আলাদা উপ-টাইপ তৈরি করা যায় ।

class Token {
public:
    virtual void printValue()=0;
};

class IntToken : public Token {
public:
    int value;
    IntToken(int ivalue) {
        value = ivalue;
    }
    virtual void printValue()override
    {
        std::cout << "The token value is: " << value << std::endl;
    }
}

0

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

এটি সংজ্ঞা অত্যধিক জটিল প্রদর্শিত হয়। তবে Token::Baseইন্টারফেসটি সংজ্ঞায়িত করে এবং ইন্টারফেস Token::Impl<>থেকে উদ্ভূত হয়। এই অভ্যন্তরীণ ক্লাসগুলি সম্পূর্ণরূপে ব্যবহারকারীর কাছে লুকানো রয়েছে Token। ব্যবহারের মত দেখতে হবে:

Token s = std::string("hello");
Token i = 7;

std::cout << "The token value is: " << s << '\n';
std::cout << "The token value is: " << i << '\n';

এছাড়াও, নীচের সমাধানটি Tokenএকটি নিয়মিত ভেরিয়েবলের জন্য একটি উদাহরণ নির্ধারণের জন্য কোনও রূপান্তর অপারেটরকে কীভাবে প্রয়োগ করতে পারে তা চিত্রিত করে । এটি নির্ভর করে dynamic_castএবং যদি কাস্টটি অবৈধ হয় তবে একটি ব্যতিক্রম ছুঁড়ে ফেলবে।

int j = i; // Allowed
int k = s; // Throws std::bad_cast

সংজ্ঞাটি Tokenনীচে রয়েছে।

class Token {

    struct Base {
        virtual ~Base () = default;
        virtual std::ostream & output (std::ostream &os) = 0;
    };

    template <typename T>
    struct Impl : Base {
        T val_;
        Impl (T v) : val_(v) {}
        operator T () { return val_; }
        std::ostream & output (std::ostream &os) { return os << val_; }
    };

    mutable std::unique_ptr<Base> impl_;

public:

    template <typename T>
    Token (T v) : impl_(std::make_unique<Impl<T>>(v)) {}

    template <typename T>
    operator T () const { return dynamic_cast<Impl<T>&>(*impl_); }

    friend auto & operator << (std::ostream &os, const Token &t) {
        return t.impl_->output(os);
    }
};

এটি অনলাইন চেষ্টা করুন!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.