Implementation of a vector of pairs
I believe you had to put typename before std::vector&lt;...&gt;::iterator to make it compile. iterator is a dependent name, thus compiler needs a hint (well, in reality it doesn't need it in context of type alias declaration, it is just how syntax is defined before C++20).

Interface

Lets address the elephant in the room first. This class trims down the features of std::vector&lt;std::pair&lt;T, U&gt;&gt; without any visible benefit. Things like std::back_inserter will not work because members are named with different name, comparison operators will not work, there are no type aliases inside the class so users will have to write template metaprogramming for simple things, and so on. 

I do not see any benefit of using this instead of std::vector&lt;std::pair&lt;T, U&gt;&gt;.

Implementation

There is no need to have void in the parameter list, empty parameter list means the functions doesn't take any parameters.

numElem looks redundant.

Access functions should be const, possibly noexcept.

Some functions are defined inline, some are not, although they are of the same complexity. This might lead to nasty linker issues.

Putting return value in () might get problematic. When using things like decltype, that will actually alter the result.

Designing interfaces

Wrappers are usually made to streamline one way of usage, possibly without losing any expressive power over original. I believe the main idea was to provide a two-way mapping from two continuous ranges. When designing such wrappers, it is important to understand under which constraints the wrappers are going to work. Let me throw some from top of my head:


The input will be in the form {index-like, index-like}
Two ranges are continuous (from 1 to 26, from 'a' to 'z' given ASCII)
It is possible to normalize the values (e.g. substract a value to make the first one 0), for example for letters it will be substract the value of 'a'
No values outside of the given ranges are ever to be queried
The mapping is one-to-one and onto, e.g. for each value on one side, there is corresponding value on the other side, and it is unique.


If the (1) and/or (2) don't hold. one will need to consider std::map and siblings.

The usage scenarios are as following:


User can somehow read/provide the mapping in the form of a range of std::pair&lt;T, U&gt; or as two separate ranges
User wants to query both ways, e.g. mapping[right] would return an object of Left type, and mapping[left] will return an object of Right type: mapping[3] == 'c' &amp;&amp; mapping['c'] == 3.
User might want to know if two mapping are equal, e.g. operator== and operator!= should be provided.
Users might want to define some relationships after the object was created, thus insertion functions like define(const Left&amp; left, const Right&amp; right) are needed. Perfect forwarding might be needed.
Users do not modify the relationships (the pair itself) after they are defined
Users might want to remove a relationship


With those, I believe something like this could fit in:

template &lt;typename Left, typename Right&gt;
class mapping
{
    // internals    
public:
    template &lt;typename InputIterator&gt;
    mapping(InputIterator first, InputIterator last); //extract mappings from iteator range

    // C++20 range version here
    // ditto for version where mappings provided as two ranges

    // rule of 0/3/5 depending on the implementation

    const Right&amp; operator[](const Left&amp; left) const; // no need for non-const version
    const Left&amp; operator[](const Right&amp; right) const;

    // returns false if either side is already in some relationship
    bool define(const Left&amp; left, const Right&amp; right) noexcept; 
    // overload for `std::pair` version

    // throws f either side is already in some relationship
    void try_define(const Left&amp; left, const Right&amp; right);
    // overload for `std::pair` version

    // returns true if the relationship existed and now removed
    bool remove(const Left&amp; left, const Right&amp; right) noexcept(destructor(Left) &amp;&amp; destructor(Right)); //pseudocode for noexcept
    // pair version
    // throwing version



    template &lt;typename Left, typename Right&gt;
    friend bool operator==(const mapping&lt;Left, Right&gt;&amp; lhs, const mapping&lt;Left, Right&gt;&amp; rhs);

    template &lt;typename Left, typename Right&gt;
    friend bool operator!=(const mapping&lt;Left, Right&gt;&amp; lhs, const mapping&lt;Left, Right&gt;&amp; rhs);

    template &lt;typename Left, typename Right&gt;
    friend void swap(mapping&lt;Left, Right&gt;&amp; lhs, mapping&lt;Left, Right&gt;&amp; rhs);
};



There are other things one might need, since those are basically sets, operations like


Merge
Difference
Intersection


might be needed for the user.