Learn C++ STL

lesson ৪ / ৯ · C থেকে যতটুকু C++ দরকার

Module ১ · C থেকে যতটুকু C++ দরকার

auto আর range-for: কম লেখা, একই মানে

Freeপড়া

এই lesson-এ যা শিখবে

  • auto লিখে variable-এর type বসানোর কাজটা compiler-কে দিতে পারবে, আর GCC 12-কে দিয়েই বলিয়ে নিতে পারবে সে কোন type বেছেছে।
  • auto কী কী বাদ দেয়, মানে const আর reference, সেটা বুঝিয়ে বলতে পারবে, আর auto& আর const auto& দিয়ে সেগুলো ধরে রাখতে পারবে।
  • Range-for-এর তিনটা রূপ লিখতে পারবে, আর copy, বদলানো আর পড়া, কোন কাজে কোনটা, বেছে নিতে পারবে।

পুরনো একটা contest solution-এ Zara এই লাইনটা পেয়েছে: for (std::vector<std::pair<std::string, int>>::iterator it = v.begin(); it != v.end(); ++it)। কাজ বলতে শুধু একটা list-এর প্রতিটা item-এ একবার করে যাওয়া, অথচ শুধু type-টাই ওর বেশিরভাগ program-এর চেয়ে লম্বা। আজকের C++ একই loop লেখে for (auto& p : v) দিয়ে। এত ছোট হলো কীভাবে, তার দুইটা টুকরো এই lesson বোঝাবে: auto আর range-for।

auto: type লেখে compiler

একটা variable যখন একটা মান নিয়ে শুরু হয়, মানটাই বলে দেয় তার type কী। 5 একটা int, 2.5 একটা double, 'z' একটা char। auto keyword compiler-কে বলে, type-টা মান থেকেই নিয়ে নাও, যাতে তোমাকে দুইবার লিখতে না হয়।

auto দিয়ে variable-এর গড়ন

auto name = initial_value;
  • auto বসে type-এর জায়গায়। Compiler এটাকে বদলে দেয় initial_value-এর type দিয়ে।
  • name তারপর থেকে ওই type-এর একদম সাধারণ একটা variable।
  • = initial_value লাগবেই: দেখার মতো কিছু না থাকলে type নেওয়ারও কিছু থাকে না।
#include <iostream>
#include <string>

int main()
{
    auto count = 3;
    auto price = 2.5;
    auto grade = 'A';
    auto name = std::string("Zara");

    std::cout << name << " bought " << count << " pens at " << price << '\n';
    std::cout << "grade " << grade << ", total " << count * price << '\n';
    return 0;
}
Zara bought 3 pens at 2.5
grade A, total 7.5

count হলো int, price একটা double, grade একটা char, আর name একটা std::string। Type ঠিক হয় program compile হওয়ার সময়, আর তারপর আর কখনো বদলায় না। তাই auto এমন কোনো বাক্স না যেটায় "যা খুশি" রাখা যায়: এটা সেই একই নির্দিষ্ট type, যেটা তুমি নিজেই লিখতে, শুধু তোমার হয়ে বসিয়ে দেওয়া।

Compiler কোন type বেছেছে, তাকে দিয়েই বলানো

Maria কথায় ভোলে না, প্রমাণ চায়। এর জন্য একটা পুরনো কৌশল আছে: variable-টা দিয়ে compiler-কে একটা অসম্ভব কাজ করতে বলো, তারপর error-টা পড়ো। বার্তায় type-এর নাম আসতেই হবে।

এখানে অসম্ভব কাজটা হলো একটা মানকে একটা খালি struct-এ বদলে ফেলা, মানে কোনো field ছাড়া struct, যেটা C++ মেনে নেয়। GCC 12 প্রতিটা লাইন ফিরিয়ে দেয়, আর প্রতিটা না-তেই বলে দেয় তাকে কী দেওয়া হয়েছিল।

struct Probe {};

int main()
{
    auto a = 5;
    auto b = 2.5;
    auto c = 'z';
    auto d = 3000000000;
    auto e = "hi";
    auto f = 7 / 2;

    Probe p1 = a;
    Probe p2 = b;
    Probe p3 = c;
    Probe p4 = d;
    Probe p5 = e;
    Probe p6 = f;
    return 0;
}

Playground-এর command line-এ GCC 12 ছয়টা error দেখায়। প্রতিটা লাইনের যে অংশটা কাজের, সেটা নিচে।

DeclarationGCC 12 যা বলেতাহলে type
auto a = 5;conversion from 'int' to non-scalar type 'Probe' requestedint
auto b = 2.5;conversion from 'double' ...double
auto c = 'z';conversion from 'char' ...char
auto d = 3000000000;conversion from 'long int' ...long: int-এর জন্য খুব বড়
auto e = "hi";conversion from 'const char*' ...একটা C string, std::string না
auto f = 7 / 2;conversion from 'int' ...int, ভিতরে 3

দুইটা সারি আরেকবার দেখার মতো। 3000000000 একটা int-এ আঁটে না, তাই literal-টা নিজেই একটা long; Playground-এর Linux-এ একটা long 8 byte। আর "hi" এখনো তোমার চেনা সেই C string, একটা const char*। এজন্যই প্রথম program লিখেছিল std::string("Zara")।

মানে auto হুবহু মানের type-টাই নেয়, তুমি কী বোঝাতে চেয়েছিলে তা নিয়ে কোনো আন্দাজ ছাড়া, আর ইচ্ছে করে একটা error ঘটালে compiler সেটা দেখিয়েও দেয়।

auto কী বাদ দেয়, আর কীভাবে ধরে রাখবে

auto নেয় মানের type, যে variable থেকে মানটা এসেছে তার সবকিছু না। দুইটা জিনিস পেছনে পড়ে থাকে: const, আর reference হওয়াটা। ফল সবসময় নতুন একটা, লেখার মতো copy।

#include <iostream>

int main()
{
    const int limit = 10;
    auto a = limit;
    a = 11;

    int x = 5;
    int& r = x;
    auto b = r;
    b = 99;
    std::cout << "a = " << a << ", x after b = 99: " << x << '\n';

    auto& c = r;
    c = 42;
    std::cout << "x after c = 42: " << x << '\n';
    return 0;
}
a = 11, x after b = 99: 5
x after c = 42: 42

a একটা সাধারণ int, তাই limit const হলেও a = 11 চলে। b হলো r-এর পেছনের মানটার একটা copy, তাই এটা বদলালে x 5-ই থাকে। Reference ধরে রেখেছে শুধু auto&, আর তখনই c = 42 গিয়ে পৌঁছেছে x-এ।

তুমি লেখোType দাঁড়ায়যা পাও
auto a = limit;intলেখার মতো একটা copy; const বাদ
auto b = r;intএকটা copy; reference বাদ
auto& c = r;int&x-এর দ্বিতীয় নাম (lesson 03)
const auto& d = x;const int&শুধু পড়ার দ্বিতীয় নাম, কোনো copy নেই

মানে সাধারণ auto সবসময় copy বানায়। দ্বিতীয় নাম চাইলে & যোগ করো, আর শুধু পড়ার দ্বিতীয় নাম চাইলে const auto&।

Range-for: প্রতিটা বাক্স, ক্রম ধরে

C-তে array ঘোরা মানে একটা index, একটা সীমা আর একটা বাড়ানো, Bob-এর off-by-one ভুলের তিনটা সুযোগ। Range-for শুধু বলে "এর প্রতিটা element-এর জন্য"। শুরু করে প্রথম বাক্স থেকে, শেষ করে শেষটার পরে, আর কোনো index-ই লাগে না।

Range-for-এর গড়ন

for (declaration : range) {
    body
}
  • range হলো যেটা ঘুরবে: এমন array যার size compiler জানে, অথবা vector-এর মতো একটা container।
  • declaration একবারে একটা element-এর নাম দেয়, যেমন int x, int& x বা const auto& x।
  • body প্রতিটা element-এর জন্য একবার চলে, প্রথম থেকে শেষ।
#include <iostream>

int main()
{
    int marks[5] = {70, 85, 62, 91, 48};
    int total = 0;

    for (int m : marks) {
        total += m;
    }
    std::cout << "total " << total << ", average " << total / 5.0 << '\n';
    return 0;
}
total 356, average 71.2

একটা কথা C জানা পাঠকের জানতেই হবে। C array-র ওপর range-for array-র প্রতিটা বাক্সে যায়: capacity ধরে, count ধরে না। 5টা নম্বর রাখা একটা int a[100] পায় 100টা পাক, যার 95টাই অব্যবহৃত বাক্সে। তাই C array-তে এটা ব্যবহার করো শুধু তখন, যখন প্রতিটা বাক্সেই data আছে, ওপরের মতো। Module 2-এর vector নিজের count জানে, এজন্যই STL lesson-গুলো সব জায়গায় range-for ব্যবহার করে।

মানে range-for প্রতিটা element-এ একবার যায়, প্রথম থেকে শেষ পর্যন্ত, আর ভুল করার মতো কোনো index থাকেই না।

তিনটা রূপ: copy, বদলানো, পড়া

Colon-এর আগের declaration ঠিক করে প্রতিটা পাকে কী আসবে। এটা আবার সেই lesson 03, আর এই ছবিতে তিনটা রূপই আছে।

একই array-র ওপর range-for-এর তিনটা রূপ for (int x : marks) copy: x নিজেই একটা বাক্স 70 85 62 copy হলো 70 x for (int& x : marks) বদলানো: x বাক্সটাই 70 x 85 62 x = 0 লেখে marks-এ for (const auto& x : marks) পড়া: বাক্সটাই, শুধু পড়ার জন্য 70 x (শুধু পড়া) 85 62 copy নেই, লেখাও নেই

তিনটা রূপ এক program-এ, প্রতিটাই একই marks-এর ওপর।

#include <iostream>

int main()
{
    int marks[3] = {70, 85, 62};

    for (int x : marks) {
        x = 0;
    }
    std::cout << "after the copy form:   " << marks[0] << ' ' << marks[1] << ' ' << marks[2] << '\n';

    for (int& x : marks) {
        x *= 2;
    }
    std::cout << "after the change form: " << marks[0] << ' ' << marks[1] << ' ' << marks[2] << '\n';

    int best = 0;
    for (const auto& x : marks) {
        if (x > best) {
            best = x;
        }
    }
    std::cout << "best, by the read form: " << best << '\n';
    return 0;
}
after the copy form:   70 85 62
after the change form: 140 170 124
best, by the read form: 170

বদলানোর রূপটা ধাপে ধাপে দেখো। প্রতিটা পাকে x পরের বাক্সটার নাম হয়, আর তার নিচে array বদলে যায়।

পাক 1: x হলো marks[0], আর marks হয় 140 85 62। পাক 2: x হলো marks[1], 140 170 62। পাক 3: x হলো marks[2], 140 170 124।

কাজের programmer-রা যে নিয়ম মানে: খেলার জন্য copy চাইলে auto x, element বদলালে auto& x, আর শুধু পড়লে const auto& x। ছোট সংখ্যার copy-তে খরচ নেই; এর চেয়ে বড় কিছু হলে copy সত্যিকারের খরচ, যেটা নিচে মাপা হয়েছে।

In C++20

Range-for নিজের ছোট একটা declaration দিয়ে শুরু হতে পারে, C-র for যেভাবে হয়, ফলে counter থাকে loop-এর ভিতরেই, আর কোথাও না। -std=c++17-এ GCC 12 এটা নেয় শুধু একটা warning দিয়ে, যে এটা C++20-এর জিনিস। তাই এই program-এ C++20-এর চিহ্ন আছে। নিচের Run button Playground খোলে C++20-এ।

#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> words = {"tea", "biscuit", "jam"};
    for (int i = 0; const auto& w : words) {
        std::cout << i << ": " << w << '\n';
        i++;
    }
    return 0;
}
0: tea
1: biscuit
2: jam
Run in Compiler

Module 0-তে দেখা container-গুলোর ওপর range-for

একই loop প্রতিটা STL container-এ চলে, আসল কথাটা এটাই। পরের module-গুলো ঠিকমতো শেখাবে এমন দুইটা container-এর ওপর একটা করে program এখানে। std::vector হলো এমন array যে নিজের size জানে (Module 2)। std::map key আর value-র জোড়া রাখে, key-র ক্রমে সাজিয়ে (Module 9)।

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> laps = {62, 58, 61, 57};
    int fastest = laps[0];

    for (int t : laps) {
        if (t < fastest) {
            fastest = t;
        }
    }
    std::cout << laps.size() << " laps, fastest " << fastest << " s\n";
    return 0;
}
4 laps, fastest 57 s
#include <iostream>
#include <map>
#include <string>

int main()
{
    std::map<std::string, int> stock = {{"tea", 12}, {"jam", 3}, {"bread", 7}};

    for (const auto& item : stock) {
        std::cout << item.first << ": " << item.second << '\n';
    }
    return 0;
}
bread: 7
jam: 3
tea: 12

Map তার item-গুলো print করেছে key-র বর্ণানুক্রমে, যে ক্রমে লেখা হয়েছিল সেভাবে না, কারণ map তার key-গুলো সাজিয়ে রাখে। প্রতিটা item একটা জোড়া, আর .first আর .second তার দুই অংশ; pair শেখাবে lesson 06। এখানে element-এর type বেশ লম্বা, std::pair<const std::string, int>, আর ঠিক এখানেই const auto& তার দাম দেয়।

মানে array, vector, map, সবগুলোর ওপর range-for পড়তে একই রকম। একবার শিখে নাও, এই track-এর প্রতিটা container এটা দিয়েই খোলে।

Copy-র ফাঁদ, মেপে দেখা

Kenji লেখে for (auto t : titles), কারণ এটাই সবচেয়ে ছোট। সংখ্যার list হলে ঠিক আছে। কিন্তু string-এর list হলে প্রতিটা পাকে body শুরু হওয়ার আগেই পুরো একটা string copy হয়।

#include <chrono>
#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> titles(100000, std::string(100, 'x'));

    auto t0 = std::chrono::steady_clock::now();
    long long a = 0;
    for (int round = 0; round < 10; round++) {
        for (auto t : titles) {
            a += t.size();
        }
    }
    auto t1 = std::chrono::steady_clock::now();
    long long b = 0;
    for (int round = 0; round < 10; round++) {
        for (const auto& t : titles) {
            b += t.size();
        }
    }
    auto t2 = std::chrono::steady_clock::now();

    std::chrono::duration<double, std::milli> byCopy = t1 - t0;
    std::chrono::duration<double, std::milli> byRef = t2 - t1;
    std::cout << "auto t:        " << a << " characters in " << byCopy.count() << " ms\n";
    std::cout << "const auto& t: " << b << " characters in " << byRef.count() << " ms\n";
    return 0;
}

List-এ আছে 100টা character-এর 100,000টা string, আর প্রতিটা loop এটা 10 বার ঘোরে। এটা Compiler Explorer-এ একবারের run, x86-64 GCC 12.2, Playground-এর flag-এ, -O2 -std=c++17।

Loopগোনা characterসময়
for (auto t : titles)10000000047.2 ms
for (const auto& t : titles)1000000001.8 ms

উত্তর একই, কিন্তু ওই run-এ 26 গুণ ধীর; পরে আরও তিনবার চালালে এসেছে 16 থেকে 22 ms, যেখানে অন্যটা 1 ms-এর মতো, মানে 15 থেকে 24 গুণ। পুরো তফাতটাই copy-র: দশ লাখ string বানানো আর ফেলে দেওয়া, শুধু এমন একটা সংখ্যা পড়ার জন্য যেটা প্রতিটা string আগে থেকেই জানত। তাই সংখ্যার চেয়ে বড় কিছু পড়ার default হলো const auto&।

auto-র যে কাজ পরে আসবে

auto function-এর return type-এর জায়গাতেও বসতে পারে, যেমন auto half(int n), আর compiler type-টা নেয় return statement থেকে। এই track এখনো সেটা ব্যবহার করে না। Function কী ফেরত দেয়, পাঠক সেটা দেখে তার প্রথম লাইনে, তাই এই module return type লিখে রাখে। Module 12 আবার auto ব্যবহার করবে, একটা lambda রাখতে, যার type লেখাই যায় না।

মানে এই module-এ auto আসে শুধু দুই জায়গায়: মান সহ একটা variable-এ, আর range-for-এর declaration-এ।

Example 1: বাজারের ঝুড়ির দাম

Alice একটা ঝুড়ির দাম যোগ করে। Array-র প্রতিটা বাক্সে একটা আসল দাম আছে, তাই এর ওপর range-for একদম ঠিক জিনিস।

#include <iostream>

int main()
{
    double prices[4] = {1.5, 2.25, 0.75, 4.0};
    double total = 0;

    for (auto p : prices) {
        total += p;
    }
    std::cout << "basket: " << total << '\n';
    return 0;
}
basket: 8.5

auto p একটা double, প্রতিটা দামের একটা copy, আর সংখ্যার copy-তে কোনো খরচ নেই। Array-তে কিছুই বদলায় না, তাই এর বেশি কিছু লাগে না।

Run in Compiler
Example 2: David সপ্তাহের তাপমাত্রা বদলে নেয়

David-এর আবহাওয়ার খাতা Celsius-এ, আর ওর কাজিনের phone দেখায় Fahrenheit। Loop প্রতিটা reading যেখানে আছে সেখানেই বদলায়, তাই লাগে auto&।

#include <iostream>

int main()
{
    double temps[5] = {21.5, 25, 18, 30, -5};

    for (auto& t : temps) {
        t = t * 9 / 5 + 32;
    }
    std::cout << "Fahrenheit:";
    for (auto t : temps) {
        std::cout << ' ' << t;
    }
    std::cout << '\n';
    return 0;
}
Fahrenheit: 70.7 77 64.4 86 23

প্রথম loop t-এর মধ্য দিয়ে array-তে লেখে; দ্বিতীয়টা শুধু copy print করে। প্রথম loop থেকে & মুছে দাও, program Celsius মানগুলোই অপরিবর্তিত print করবে, আর GCC 12 কোনো command line-এই কিছু বলবে না।

Run in Compiler
Example 3: Amara-র পড়ার তালিকা, প্রথম অক্ষর ধরে

Amara প্রতিটা বইয়ের নাম এক শব্দে লিখে তালিকা রাখে। ও চায়, একটা নির্দিষ্ট অক্ষর দিয়ে শুরু হওয়া নামগুলো, সাথে তাদের সংখ্যা। Program তালিকাটা পড়ে একটা vector-এ, যে নিজের size জানে (Module 2), আর আবার পড়ে const auto& দিয়ে।

#include <iostream>
#include <string>
#include <vector>

int main()
{
    int n = 0;
    std::cin >> n;

    std::vector<std::string> titles(n);
    for (auto& t : titles) {
        std::cin >> t;
    }

    char letter = ' ';
    std::cin >> letter;

    int found = 0;
    for (const auto& t : titles) {
        if (t[0] == letter) {
            std::cout << t << '\n';
            found++;
        }
    }
    std::cout << found << " of " << titles.size() << " start with " << letter << '\n';
    return 0;
}
Dune
Dracula
2 of 5 start with D

এই output input 5, তারপর Dune Emma Dracula Matilda Ulysses, তারপর D-এর জন্য। প্রথম range-for বাক্সগুলো ভরে, তাই ওর লাগে auto&, যেভাবে std::cin >>-এর লাগে লেখার মতো একটা বাক্স। দ্বিতীয়টা শুধু পড়ে, তাই const auto& পাঁচটা string-এর copy বাঁচায়। Zara এমন একটা অক্ষর দিয়ে দেখত যেটা দিয়ে কোনো নামই শুরু হয় না; program তখন print করে 0 of 5 start with Q।

Run in Compiler

এটা কোথায় কাজে লাগে

  • C++ Core Guidelines। Rule ES.11 বলে type-এর নাম বারবার না লিখে auto ব্যবহার করতে, আর ES.71 বলে সুযোগ থাকলে সাধারণ for-এর চেয়ে range-for বেছে নিতে।
  • LLVM coding standards। LLVM compiler project-এর নিয়মে একটা অংশের শিরোনামই "Beware unnecessary copies with auto"। এটাই ওপরের copy-র ফাঁদ, আর সেখানে বলা আছে, copy ইচ্ছে না হলে auto& লিখতে।
  • Google-এর C++ style guide। Project-এ নতুন কোনো পাঠকের কাছে code পরিষ্কার হলে এটা type deduction-এর অনুমতি দেয়। আর জরুরি কোনো type আড়ালে চলে গেলে সাবধান করে। Chromium-এর C++ style এই guide-এর ওপরেই দাঁড়িয়ে।
  • এর পরের প্রতিটা STL module। প্রতিটা container lesson তার container ঘোরে range-for আর const auto& দিয়ে, Module 2-এর vector থেকে Module 10-এর hash map পর্যন্ত।

যে ভুলগুলো সবাই করে

১. Range-for-এ copy বদলানো।

for (auto x : marks) {
    x *= 2;
}

কোনো command line-এই কোনো বার্তা নেই, Playground-এর-টাতেও না, -Wall -Wextra-তেও না। Program তারপর print করে 70 85 62, যেমন ছিল তেমন। লেখো auto& x। এই ভুলটা তুমি করবে, কারণ auto x রূপটাই সবচেয়ে বেশি চোখে পড়ে, আর শুধু পড়ার জন্য copy-তে কোনো সমস্যা নেই।

২. দেখার মতো কিছু ছাড়াই auto।

auto x;
x = 5;

প্রতিটা command line-এ error: error: declaration of 'auto x' has no initializer। Type আসে মান থেকে, তাই মানটা একই লাইনে থাকতে হবে। লেখো auto x = 5;, আর মান পরে এলে type নিজে লিখে দাও।

৩. চলতি যোগফলের type 0-কে বেছে নিতে দেওয়া।

auto total = 0;
total += 2.5;
total += 2.5;

কোনো command line-এই কোনো বার্তা নেই, আর total print হয় 4, 5 না। 0 একটা int, তাই total চিরকালের জন্য int, আর প্রতিটা 2.5 কেটে ছোট করে বসানো হয়: 2.5 হয় 2, তারপর 4.5 হয় 4। লেখো double total = 0; বা auto total = 0.0;। যেখানে যোগফলের long long লাগে, সেখানেও একই ফাঁদ একটা int ধরিয়ে দেয়।

৪. Parameter হয়ে আসা array-র ওপর range-for।

void printAll(int a[])
{
    for (int x : a) {
        std::cout << x << ' ';
    }
}

প্রতিটা command line-এ error: error: 'begin' was not declared in this scope; did you mean 'std::begin'?, আর end-এর জন্যও একই। পরামর্শটা ভুল পথে নেয়: আসল কারণ হলো array parameter আসলে একটা pointer, C-র মতোই, আর pointer-এর ঘোরার মতো কোনো size নেই। Size পাঠাও আর index loop ব্যবহার করো, অথবা vector পাঠাও (Module 2)।

মাথা খাটাও

Kenji braces-এর ভিতরে তিনটা সংখ্যা লিখে auto-কে ঠিক করতে দেয়।

#include <iostream>

int main()
{
    auto x = {1, 2, 3};
    std::cout << x.size() << '\n';
    return 0;
}

কোনো বার্তা ছাড়াই compile হয়, আর print করে 3। তাহলে x একটা int না, আবার C array-ও না, কারণ C array-র কোনো .size() নেই। এটা তাহলে কী, আর এই track ঠিক এই এক জায়গায় type নিজে লিখতে বলে কেন?

"Compiler কোন type বেছেছে, তাকে দিয়েই বলানো" অংশের কৌশলটা x-এর ওপর চালাও, তারপর বার্তাটা পড়ো।

অনুশীলন ১সহজ

বছরের একটা মজার দিনে এক দোকান সব দাম দ্বিগুণ করে দেয়, আর দামের তালিকা ঠিক করার দায়িত্ব Bob-এর। প্রতিটা দাম যেখানে আছে সেখানেই বদলাতে হবে, নতুন তালিকা বানানো চলবে না। Starter দামগুলো পড়ে রাখে std::vector<long long> values-এ, এমন array যে নিজের size জানে। Range-for দিয়ে প্রতিটা মান জায়গায় দ্বিগুণ করো, তারপর তালিকাটা print করো।

Input. প্রথম লাইনে n। দ্বিতীয় লাইনে n-টা integer।

Output. এক লাইনে n-টা integer, প্রতিটা দ্বিগুণ, input-এর ক্রমে, মাঝে একটা করে space।

Constraints. 1 <= n <= 80000। প্রতিটা integer -2000000000 থেকে 2000000000-এর মধ্যে।

Sample. Input 4 আর 3 -1 0 2000000000 দিলে আসে 6 -2 0 4000000000।

#include <iostream>
#include <vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int n = 0;
    std::cin >> n;

    // A vector is an array that knows its own size (Module 2 teaches it),
    // so a range-for over it walks exactly the n values.
    std::vector<long long> values(n);
    for (long long& x : values) {
        std::cin >> x;
    }

    // Double every value in place with a range-for. Then print the
    // values on one line, separated by single spaces.

    return 0;
}

doubled নামে গ্রেড হয়। Hidden test-এ আছে n = 1, একা একটা -2000000000, আর 2000000000-এর 80000টা মান, যাদের দ্বিগুণ একটা int-এ আঁটে না। যে loop copy দ্বিগুণ করে, সে input-ই অপরিবর্তিত print করে, আর শুধু 0-ওয়ালা test-টা ছাড়া বাকি সব ফেল করে।

Run in Compiler
অনুশীলন ২মাঝারি

Maria একটা শব্দের খেলা খেলে, আর ও যত শব্দ পেয়েছে তার মধ্যে সবচেয়ে লম্বাটা চায়। সবচেয়ে লম্বা শব্দ আর তার দৈর্ঘ্য print করো। একাধিক শব্দের দৈর্ঘ্য সমান সবচেয়ে বেশি হলে, তাদের প্রথমটা print করো।

Input. একটা লাইনে n, তারপর space দিয়ে আলাদা করা n-টা শব্দ।

Output. এক লাইন: সবচেয়ে লম্বা শব্দ, একটা space, আর তার দৈর্ঘ্য।

Constraints. 1 <= n <= 1000। প্রতিটা শব্দ 1 থেকে 30টা ছোট হাতের অক্ষর।

Sample. Input 4 আর tea biscuit jam toffee দিলে আসে biscuit 7।

#include <iostream>
#include <string>
#include <vector>

int main()
{
    int n = 0;
    std::cin >> n;

    // A vector of n strings (Module 2), read with a range-for.
    std::vector<std::string> words(n);
    for (auto& w : words) {
        std::cin >> w;
    }

    // Walk words with a range-for and keep the longest one.
    // A string's length is w.size().

    return 0;
}

আলাদা করে গ্রেড হয় না। Loop-এর declaration বেছে নাও এই lesson-এর নিয়ম মাথায় রেখে: তুমি শব্দগুলো শুধু পড়ছ।

Run in Compiler

সচরাচর যে প্রশ্নগুলো আসে

  • auto কি Python বা JavaScript-এর variable-এর মতো, যেটায় যা খুশি রাখা যায়?

    না। ওই ভাষাগুলোতে একটা variable এখন সংখ্যা রাখতে পারে, পরে string। auto variable program compile হওয়ার সময় একটা type পায়, আর সেটাই রাখে। ভুল ৩ ঠিক এই নিয়মেরই কামড়: total int-ই থেকে গেল।

  • auto কি আমার program ধীর করে দেয়?

    না। তুমি যে type লিখতে, compiler সেটাই বসায়, তাই machine code একই। খরচ শুধু যেটা এই lesson মেপেছে, আর সেটা আসে copy থেকে, auto থেকে না।

  • Type কখন নিজে লিখব?

    যখন মানটা তোমার আসল উদ্দেশ্য বলে না। যেমন 0, যেখানে total-এর লাগবে long long বা double; একটা C string, যেখানে তুমি চাও std::string; আর মাথা খাটাও-এর braces। আর তখনো, যখন type ছোট আর পাঠককে কিছু জানায়, যেমন int count = 0;।

  • Range-for-এর ভিতরে index পাব কীভাবে?

    Range-for-এর কোনো index নেই। পাশে একটা counter রাখো, যেমন "In C++20" block করেছে, অথবা index-টাই যখন আসল কথা, তখন index loop ব্যবহার করো। দুইটাই ঠিক; যেটা পড়তে সহজ, সেটা নাও।

  • marks const না হলেও for (const auto& x : marks) লেখা যায় কেন?

    const যোগ করা মানে শুধু একটা ক্ষমতা ছেড়ে দেওয়া, তাই এটা সবসময় চলে। উল্টোটা চলে না: একটা const int marks[4]-এর ওপর auto& m দিয়ে লেখার চেষ্টা করলে GCC 12 দেয় error: assignment of read-only reference 'm', কারণ auto& যার নাম, তার const ধরে রাখে।

মূল কথা

  • auto x = value; x-কে মানের type দেয়, compile-এর সময়েই ঠিক করা; এটা "যেকোনো type" না।
  • খালি একটা struct-এ assign করতে গেলে GCC 12 print করে auto কোন type বেছেছিল।
  • সাধারণ auto const আর reference বাদ দিয়ে copy বানায়; auto& আর const auto& সেগুলো ধরে রাখে।
  • Range-for প্রতিটা element-এ একবার যায়, প্রথম থেকে শেষ; C array-তে তার মানে প্রতিটা বাক্স, ব্যবহার হোক বা না হোক।
  • Copy-র জন্য auto x, বদলানোর জন্য auto& x, আর সংখ্যার চেয়ে বড় কিছু পড়ার জন্য const auto& x।

এরপর lesson 05 বোঝাবে std::vector<long long>-এর angle bracket-গুলো: একটা template, একটা type দিয়ে ভরা।

lesson ৪ শেষ

শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।

পরেরটা: template-এর ধারণা: একটা function, সব type-এর জন্য

auto আর range-for: কম লেখা, একই মানে | Learn C++ STL | Progsity