2
#include <bits/stdc++.h>
using namespace std;

#define vi vector<int>
#define vvi vector<vi>

int main() {
    vi v(10, -1);
    vvi vv(10, v);
    for(int i=0; i<vv.size(); i++){
        for(int j=0; j<vv[i].size(); j++){
            cout << vv[i][j] << " ";
        }
        cout << endl;
    }
}

When I compile the above code, no error is reported and it runs fine (see this). But when I declare a vector of vector of int using vector < vector < int>> an error occurs because I have not put a space between the right angle brackets. So why is no error reported in the first case when all that #define does is replace the occurrences of vi with vector<int> and vvi with vector< vector< int>>?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Nikunj Banka
  • 11,117
  • 16
  • 74
  • 112

2 Answers2

8

The reason you're not getting the error with the #define method is that the preprocessor is intelligently inserting breaks. If you pass that code through only the preprocessor stage (gcc -E, for example), you'll see something like:

int main() {
    vector<int> v(10,-1);
    vector<vector<int> > vv(10,v);           // <<-- see space here.
    for(int i=0; i<vv.size(); i++){
        for(int j=0; j<vv[i].size(); j++){
            cout << vv[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

The reason why this happens has to do with the phases of translation as dictated by the ISO C++ standard.

Section 2.2 of C++11 (a) states that there are nine translation phases. Phase 3 is the splitting of the source file into preprocessing tokens and white space.

The important thing there is the tokenisation, so that vector<vi> is the set of preprocessing tokens {vector, <, vi, >}, it is not the simplistic text given in the macro replacement section. While a simplistic text substitution of:

#define vi vector<int>
#define vvi vector<vi>
vvi xyzzy;

would result in:

vector<vector<int>> xyzzy;

what you actually end up with is the preprocessing token set:

{vector, <, vector, <, int, >, >, WHITESPACE, xyzzy, ;}

Then, at phase 7, the standard states:

Each preprocessing token is converted into a token.

So, there's no recombining of the two > tokens into a single one despite the fact a simple reading of the source may suggest that.


(a) Keep in mind that, though I'm quoting the C++11 parts of the standard to ensure the answer is more up-to-date, this particular problem is a C++03 one. C++11 actually fixes the parser so that multiple > characters will close a template argument list where reasonable (C++03 always treated it as the right-shift operator).

C++11 14.2, section 3 specifies this:

When parsing a template-argument-list, the first non-nested > is taken as the ending delimiter rather than a greater-than operator. Similarly, the first non-nested >> is treated as two consecutive but distinct > tokens ...

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

The error occurs because in C++03, the double angle brackets are interpreted as the right shift operator >>. In C++11, the language was altered so that it is interpreted as closing the template arguments instead, so it should compile without errors in C++11 mode. You can work around this by putting a space between the closing angle brackets, e.g. vector<vector<int> >.

Also, don't use #define to define type aliases like this; use typedef instead, as that's exactly what it's intended for.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • Adam you are correct in saying that no errors will be reported in C++11 but I am working on C++ 4.8.1 . However I have got the answer in paxdiablo's answer. – Nikunj Banka Feb 17 '14 at 06:58