Rating changes for last rounds are temporarily rolled back. They will be returned soon. ×

Hikikomorichka's blog

By Hikikomorichka, history, 3 years ago, In English

I recently came across some very cool C++ tricks from some of the best coders which can help increase your code efficiency as well as make the code very neat , clean and easy to understand.I would be sharing some of these here.

1.Normally to find minimum among few elements we use something like this.

int a = min(x1,min(x2,min(x3,min(x4,x5))));

Instead of this we can use this.

int a = min({x2,x2,x3,x4,x5});

2.Using emplace_back in place of push_back

In C++ 11,emplace_back works just like push_back adding elements at the end of a vector. emplace_back is faster than push_back as push_back first creates a temporary variable and then adds it to the end of vector.

vector<int> v;
v.push_back(1); // Slower
v.emplace_back(2); //Faster

3.Using tuples

//Normally we use something like this
pair<int,pair<int,pair<char,int > > > p;
p=make_pair(1,make_pair(2,make_pair('a',3)));
// To access different elements we use something like this.
// TO access 1
int x=p.first;
// To access 2
int y=p.second.first;
//To access 'a'
char z=p.second.second.first;
//TO access 3
int s=p.second.second.second

// Instead of the above usage we can use tuples

tuple(int,int,char,int) t1=make_tuple(1,2,'a',3);
// TO access the ith element of a tuple we can use get<i> nameOfTuple
cout << get<0> t1 <<"  "<<get<2>t1<<endl; // Prints 1 and 'a'

4.Lambda Functions

Yes we have Lambda functions in C++ as well.Lambda functions are functions which do not have a name.Normally when we have to write a comparator function for usage whenever we want to sort an array according to a certain rule.Sometimes these functions are very simple and we do not need to explicitly write a comparator function for that.This can be simply done using lambda function.

bool cmp(int a,int b){
 return a>b;
}

vector<int> v;
v.emplace_back(1);
v.emplace_back(101);
v.emplace_back(5);
v.emplace_back(12);
v.emplace_back(-4);

// Now sorting

sort(v.begin(),v.end(),cmp); 


// The above statement can be simple written without the comparator function
// using the lambda function

sort(v.begin(),v.end(),[](int a,int b){return a>b;}); 

// A lambda function is written like   [](argument1,argument2,.....){//code}

5.Using Conditional Operators

Using conditional operators can make the code clean and efficient.

// Without conditional operators
if (a & 1) { // when a is odd
  a=a*2;
}
else{
  a=a+1;
}

// With conditional operators

a&1?a*=2:a+=1;

6. Bit Manipulation

// Conventional way to check ith bit set or not.
bool isSet(int num,int i){ // bit is counted from lsb to msb ..int this order 0th bit,1st bit...and so on
   for(int j=0;j<i;j++){
      num=num/2;
   }
  if(num&1)return true;
  return false;
}

// Using shift operator 
int t= num & (1<<i); // t is non zero (2^(i)) to be exact if ith bit is set else 0
// Setting ith bit
num|=(1<<i);
// flipping ith bit
num ^ = (1<<i); // ^ is the xor operator
// clearing the ith bit
num=num & ~(1<<i);

7.Using auto

Auto automatically determines the data type of the variable at run time and hence you need not to specify the data type of the variable.It also makes iterating over containers easy(code wise efficient).

 auto a = 1; // a will become 'int'
    auto b = 1LL; // b will become 'long long'
    auto c = 1.0; // c will become 'double'
    auto d = "variable"; // d will become 'string'


    vector<int > v;
    v.push_back(5);
    v.push_back(10);
    v.push_back(13);
    // or instead you can write vector<int>v={5,10,13};
    //  Now to print the contents of v  you can use
    for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) 
        cout << *it << ' ';
    // or you can use auto keyword to ease your work
    for(auto n:v)
    cout<<n<<" ";
    // Same goes for set , map and other data structures.
    set<pair<int,int> >s;
    s.insert({1,2}) // or you can use s.insert(make_pair(1,2))
    s.insert({5,6}) //  or s.insert(make_pair(5,6))
    // or simply you can write set<pair<int,int> >s={ {1,2} , {5,6} }
    // for printing the elements present in s
    for (set<pair<int,int>>::iterator it = s.begin(); it != s.end(); ++it) 
    cout << (*it).first << '  '<<(*it).second<<"\n";
    // or you can use auto to print its elements
    for(auto x:s)
    cout<<x.first<<" "<<x.second<<"\n";
  • Vote: I like it
  • +13
  • Vote: I do not like it

| Write comment?
»
3 years ago, # |
Rev. 3   Vote: I like it +77 Vote: I do not like it

\2. This is a completely wrong example of emplace_back usage, no one would use C++ if compilers couldn't optimize push_back(int) or even push_back(pair<int, int>). emplace_back is used for two reasons: a) it's sometimes shorter, b) it's faster when the elements are containers themselves.

vector<pair<int, int>> arr;
arr.push_back(make_pair(1, 2)); // good
arr.emplace_back(make_pair(1, 2)); // same speed
arr.emplace_back(1, 2); // same speed but much shorter

emplace_back forwards its arguments right to the constructor so you can use 1, 2 and it'll be the same as pair<int, int>(1, 2) (almost, because the latter also calls move constructor but oh well).

Here's when emplace_back is slightly faster:

vector<vector<int>> arr;
arr.push_back(vector<int>{1, 2, 3}); // constructs vector<int> and then moves it to the outer container
arr.emplace_back(vector<int>{1, 2, 3}); // same
arr.emplace_back(initializer_list<int>{1, 2, 3}); // faster, perfect-forwards the data right to the constructor
»
3 years ago, # |
  Vote: I like it +53 Vote: I do not like it

\3. You mistyped the tuple syntax. It should be:

tuple<int, int, char, int> t1 = make_tuple(1, 2, 'a', 3);
// To access the ith element of a tuple we can use get<i>(nameOfTuple)
cout << get<0>(t1) <<" " << get<2>(t1) << endl; // Prints 1 and 'a'

There are three points of interest:

a) It's tuple<int, int, char, int>, not tuple(int, int, char, int) because these are template parameters, not function parameters.

b) get<I>(x) is no magic, it's just a template function get<I> called with a single argument x. There is no special syntax here.

c) No one uses make_tuple these days, you should try initializer lists:

tuple<int, int, char, int> t1{1, 2, 'a', 3};

You could argue it's better to use auto and make_tuple like this, but personally I prefer making sure variable types are expected.

»
3 years ago, # |
  Vote: I like it +45 Vote: I do not like it

In this statement:

auto d = "variable"; // d will become 'string'

d will actually become const char*

»
3 years ago, # |
  Vote: I like it +25 Vote: I do not like it

\4. The point of lambdas has never been having 'functions which do not have a name'. C++ lambdas simplify variable capture. Here's what it means:

Pretend that you want to sort an array, but instead of comparing values themselves, you compare priority_of[value]. In Python it's really simple:

priority_of = ...
a.sort(key=lambda value: priority_of[value])

What about C++?

void solve() {
    ...
    auto priority_of = ...
    sort(a.begin(), a.end(), uh?..);
    ...
}

Hm, so what do you pass as a comparator? Maybe an external function?

void compare(int i, int j) {
    return priority_of[i] < priority_of[j];
}
void solve() {
    ...
    auto priority_of = ...
    sort(a.begin(), a.end(), compare);
    ...
}

Oops, priority_of is a local variable so compare can't access it. Of course, you could make it global but that might become a problem if you use recursion, and copying is slow. You could also try pointers... but lambdas provide a simple solution:

void solve() {
    ...
    auto priority_of = ...
    sort(a.begin(), a.end(), [&priority_of](int i, int j) {
        return priority_of[i] < priority_of[j];
    });
    ...
}

See this magic, [&priority_of]? This is called capture list. & before variable name means that the list is passed by reference and is not copied. You can shorten the capture list like [&], but this means all variable references are stored so it may be a bit slower.

  • »
    »
    3 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Actually, using [&] will only capture the references to the variables you call inside the lambda, so it will be the same speed.

    • »
      »
      »
      3 years ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      Unfortunately I don't have any code at the moment but I remember that replacing [&] with [&var] helped me once.

      Here's another idea. When you capture an integer by reference, this means a pointer to it is stored. When you access this variable later, it takes a bit more time than reading the integer directly. So it's better to capture primitives by value and other variables by reference, like this:

      int x;
      vector<int> a;
      
      auto lambda = [x, &a](...) {
          ...
      };
      
»
3 years ago, # |
  Vote: I like it +18 Vote: I do not like it

You don't need to use auto for range-based for loops. You can just use like this:

vector<int> v = {5, 10, 13};
for (int i : v) cout << i << " ";

The situation where auto is useful here is if you want to iterate using iterators:

vector<int> v = {5, 10, 13};
for (auto it = v.begin(); it != v.end(), it++)
    cout << *it << " ";

Also, since C++17, you can do this:

set<pair<int,int>> s;
s.insert({1,2});
s.insert({5,6});
for (auto [a, b] : s) cout << a << " " << b << endl;
  • »
    »
    3 years ago, # ^ |
    Rev. 2   Vote: I like it 0 Vote: I do not like it

    A bit offtopic but I think I should mention that this:

    s.insert({1, 2});
    s.insert({5, 6});
    

    is identical to this:

    s.emplace(1, 2);
    s.emplace(5, 6);
    

    except that the latter is a bit faster for non-primitives.

»
3 years ago, # |
Rev. 2   Vote: I like it +35 Vote: I do not like it

\5. Oh god, please don't write any such code ever!

This is not 'clean and efficient' code. It's horribly unreadable, especially if you don't use spaces, and a usual if..else construct is as fast as a conditional operator.

Instead of this:

a&1?a*=2:a+=1;

Use this:

if(a & 1) {
    a *= 2;
} else {
    a++;
}

Or, even better:

if(a % 2 == 1) {
    a *= 2;
} else {
    a++;
}

This is as fast as a & 1 but (arguably) more readable and more obvious to other code readers.

Here's the case when conditional operators actually simplify something. Pretend that you want to calculate 2D prefix sums. Here's how I usually do it:

for(int i = 0; i < n; i++) {
    for(int j = 0; j < m; j++) {
        pref[i][j] = (
            a[i][j]
            + (i == 0 ? 0 : pref[i - 1][j])
            + (j == 0 ? 0 : pref[i][j - 1])
            - (i == 0 || j == 0 ? 0 : pref[i - 1][j - 1])
        );
    }
}

Without conditional operators, the code would take more lines, and the +/+/+/- structure wouldn't be as obvious.

  • »
    »
    3 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    In addition: if-else is more convenient for debugging because you can see which branch is it going to.

  • »
    »
    3 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Why don't you use a 1-indexed 2-D prefix sum, that eradicates the out of bounds issue with case i=0/j=0

    • »
      »
      »
      3 years ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      I just want pref to have the same size and indexing as a. Of course, you could make a 1-indexed too but that may require me to modify other algorithms, etc... so I just use 0-indexation everywhere and only fix prefix sums.

  • »
    »
    3 years ago, # ^ |
    Rev. 2   Vote: I like it -14 Vote: I do not like it

    [DELETED]

    • »
      »
      »
      3 years ago, # ^ |
        Vote: I like it +5 Vote: I do not like it

      Conditional operator was not invented for statements, it's for expressions. You shouldn't use a&1 ? a*=2 : ++a, but you may use a = a&1 ? a*2 : a+1. This way you explicitly state the intent: update a variable (a =) depending (?) on some condition (a&1). (as a sidenote, I also hate it when people write if(x) a++,b++; instead of if(x) { a++; b++; })

      You're right in the second example, code 1 is cleaner than code 2, except that I'd replace (string)"IMPOSSIBLE" with "IMPOSSIBLE"s. Here you use ?: on expressions, not statements, and it works well here. (it'd be even better if C++ could infer the type of n == -1 ? "IMPOSSIBLE" : n expression as std::variant<char*, int> and was able to print it then, but sadly C++ is too strict)

»
3 years ago, # |
  Vote: I like it 0 Vote: I do not like it

U deserve a standing ovation

»
3 years ago, # |
  Vote: I like it 0 Vote: I do not like it
    auto d = "variable"; // d will become 'string'

d will become a const char* (c string) NOT std::string, if you want it to become an std::string you need to use std::string's constructor: string(...) example:

auto d = string("variable")
  • »
    »
    3 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    But then it's just shorter to write

    string d = "variable";
    
»
3 years ago, # |
  Vote: I like it +31 Vote: I do not like it

This blog contains too much fake news and should be removed, because is harmful for inexperienced coders.

  • »
    »
    3 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    The up- and downvote system is supposed to do that. Unfortunatly it does not allways work in the right direction :/

»
3 years ago, # |
  Vote: I like it +5 Vote: I do not like it

This comment section makes me feel like: Cunningham's Law states "the best way to get the right answer on the internet is not to ask a question; it's to post the wrong answer." xD

Still I really like these tricks and the comments were very helpful!