Ambiguous Call - Templated Function - c++

I've been trying to brush up on my C++ knowledge and found this issue I cannot Google for easily.
I've written a function with the following signature:
template <typename Iterator>
Iterator lower_bound(
Iterator left,
Iterator right,
typename Iterator::value_type const& target
) {
//stuff happens here
}
and I'm trying to use it like so:
std::vector<int> odd{1, 2, 3, 4, 5, 6, 7};
int i = 4;
std::cout << std::distance(odd.begin(), lower_bound(odd.begin(), odd.end(), i)) << std::endl;
I'm getting the following compilation error: error: call to 'lower_bound' is ambiguous.
I'm guessing I'm lacking some understanding about how template types are resolved and so would be grateful for any resources that might explain this in a bit more detail.

Here, std::lower_bound is causing the ambiguity. Due to Argument-dependent lookup your function as well as the std version are around.
If you use your own (non-std) iterator type, then you would not face any problem. See the example below (live demo):
#include <vector>
#include <iostream>
template <class T>
struct myIter {
using value_type = T;
myIter(T* const ptr) : ptr(ptr) {};
T& operator*() {return *ptr;};
T* ptr;
};
template <typename Iterator>
Iterator lower_bound(
Iterator left,
Iterator right,
typename Iterator::value_type const& target)
{
return left;
}
int main()
{
std::vector<int> odd{1, 2, 3, 4, 5, 6, 7};
myIter<int> b(&(odd.front())), e(&(odd.back()));
int i = 4;
std::cout << *(lower_bound(b,e, i)) << std::endl; // <- works
// std::cout << *(lower_bound(odd.begin(),odd.end(), i)) << std::endl; // <- compiler error
}

Related

In template<class It> function where It is an iterator, can I make It::value_type work for both vector::iterators and array::iterators?

I have been writing some functions that take a template called It, which should be an iterator. Then I use the It::value_type for some functionality. This has been working for most containers I have tried but fails for std::array. If I do use an std::array I get the error
error: ‘long unsigned int*’ is not a class, struct, or union type
So I see the problem, the iterator for the std::array is just a pointer, which makes sense to me. Therefore it has no ::value_type defined. But how can I make my templated code so that this works for std::array and std::vector, std::list etc?
I made a MWE, the function in it just a silly pathological example that shows my problem
#include <vector>
#include <iostream>
#include <array>
template <class It>
void print_accumulate(It first, It last) {
typename It::value_type result{}; // What do I write here??
while (first != last) {
result += *first;
++first;
}
std::cout << result << "\n";
}
int main() {
std::vector<size_t> v = {1, 2, 3, 4, 5}; /* Replacing with std::array<size_t, 5> gives error */
print_accumulate(v.begin(), v.end());
}
The above works just fine for just about every container I've tried, vector, list, set etc. However, when I try to run the code by replacing the std::vector<size_t> with std::array<size_t, 5>, i get the error message I gave.
Thanks in advance!
Use iterator_traits
template <class It>
void print_accumulate(It first, It last) {
typename std::iterator_traits<It>::value_type result{}; // use iterator_traits
while (first != last) {
result += *first;
++first;
}
std::cout << result << "\n";
}

Why decltype() yelds a pointer to reference?

I'm trying to get a better knowledge about iterator_traits and found a snippet of code on a C++ textbook. So I've added some code to make it executable with the aim to understand the behaviour of that said compile-time mechanism. The complete program looks like so:
template<typename C>
using Iterator_type = typename C::iterator;
template<typename Iter>
using Iterator_category = typename std::iterator_traits<Iter>::iterator_category;
template<typename Ran>
void sort_helper(Ran beg, Ran end, std::random_access_iterator_tag)
{
sort(beg, end);
}
template<typename For>
void sort_helper(For beg, For end, std::forward_iterator_tag)
{
std::vector<decltype(*beg)> v {beg, end};
sort(v.begin(), v.end());
copy(v.begin(), v.end(), beg);
}
template<typename C>
void sort(C& c)
{
using Iter = Iterator_type<C>;
sort_helper(c.begin(), c.end(), Iterator_category<Iter>{});
}
void test(std::vector<std::string>& v, std::forward_list<int>& lst)
{
sort(v);
sort(lst);
}
template<typename C>
void print(C& c)
{
for(auto it = c.begin(); it != c.end(); ++it) {
std::cout << *it << '\n';
}
std::cout << '\n';
}
int main()
{
std::vector<std::string> v { "Bicycle", "Hoovercraft", "Rocket", "Airplaine", "Bus", "Submarine" };
print(v);
sort(v);
print(v);
std::forward_list<int> lst { 2, 81, 3, 0, 4, 34, 23, 11 };
print(lst);
sort(lst);
print(lst);
return 0;
}
The compilation fails telling that a pointer to reference is given:
memory:1723:16: error:
'pointer' declared as a pointer to a reference of type 'int &'
typedef _Tp* pointer;
and the error stack brings to the following line:
in instantiation of template class 'std::__1::vector<int &, std::__1::allocator<int &> >'
requested here
std::vector<decltype(*beg)> v {beg, end};
this compiling either on Mac OS and on a Linux machine with g++.
I managed it to work modifying the involved part like so:
auto i = *beg;
std::vector<decltype(i)> v {beg, end};
Can you please give me a clearer explanation on why the decltype() function is returning a pointer to a reference? I've read some other questions regarding the decltype type function on SO and also have read the specifications but neither brought me on the right path, nor examining the full compiler stack of output messages was of any help.
Thank you in advance for any consideration.
The problem is that the decltype yields a reference type (you cannot make std::vector of).Why is that so is answered here in-depth.
You can fix it by either of the two ways:
use std::remove_reference<decltype(*beg)>::type
use std::iterator_traits<For>::value_type

C++ Why does error "no matching function" appear when it 100% looks like they match?

I don't understand why I'm getting an error that states my function doesn't match my defined template function. To me, they look exactly the same. Here is the error in my debug:
error: no matching function for call to 'mergesort' newVec = mergesort(vec.begin(),vec.end());
So I can learn and write better generic functions and templates, what do I need to change to make that error go away? (Just to be clear, I'm not asking for help with my mergesort algorithm - I know it has problems, but I'll work them out.)
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
vector<T> mergesort(typename vector<T>::iterator, typename vector<T>::iterator);
int main() {
vector<int> vec{ 50, 5, 40, 10, 30, 15, 20, 20, 10, 25 };
vector<int> newVec;
newVec = mergesort(vec.begin(),vec.end()); //Doesn't match???
cout << "before:";
for (auto x : vec) cout << x << ' '; cout << '\n';
cout << "after :";
for (auto x : newVec) cout << x << ' '; cout << '\n';
return 0;
}
template <typename T>
vector<T> mergesort(typename vector<T>::iterator begin, typename vector<T>::iterator end){
vector<T> newVector;
copy(begin, end, newVector);
if(begin!=end){
vector<T> tmp1;
vector<T> tmp2;
auto dist = distance(newVector.begin(),newVector.end());
auto distance1 = dist/2;
auto distance2 = (dist/2+1);
auto mid1 = newVector.begin();
auto mid2 = newVector.begin();
advance(mid1,distance1);
advance(mid2,distance2);
tmp1 = mergesort(newVector.begin(), mid1);
tmp2 = mergesort(mid2, newVector.end());
merge(tmp1.begin(), mid1, mid2, tmp2.end(), newVector.begin());
return newVector;
} else {
return newVector;
}
}
The problem is that this is a non-deduced context:
template <typename T>
vector<T> mergesort(typename vector<T>::iterator, typename vector<T>::iterator);
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
There are two good answers for why this function call fails to compile.
As to how to fix it - even if the above code did work, there's no reason to limit your functionality to vector iterators anyway. You can merge-sort other containers too. So just deduce any iterator:
template <typename Iterator>
vector<T> mergesort(Iterator, Iterator);
Moreover, typically we'd typically expect the merge to be modifying the range provided, not returning a new container. So really prefer:
template <typename Iterator>
void mergesort(Iterator, Iterator);
There's a few other issues in your code. The call to std::merge() here:
merge(tmp1.begin(), mid1, mid2, tmp2.end(), newVector.begin());
newVector is empty before this call, so we're just going to overwrite memory that it doesn't own. You'll want to do:
newVector.reserve(dist);
merge(tmp1.begin(), mid1, mid2, tmp2.end(), std::back_inserter(newVector));

How to construct Boost bimap from static list?

I have a bimap like this:
using MyBimap = boost::bimaps::bimap<
boost::bimaps::unordered_set_of<A>,
boost::bimaps::unordered_set_of<B>>;
I want to construct it from a static initializer list, as it can be done for std::map:
MyBimap map{{a1, b1}, {a2, b2}, {a3, b3}};
Unfortunately, it doesn't work because bimap doesn't support initializer lists, so I tried a workaround. Boost's documentation lists the following constructors:
bimap();
template< class InputIterator >
bimap(InputIterator first,InputIterator last);
bimap(const bimap &);
So I tried the second one, like this:
std::vector<std::pair<A,B>> v{{a1, b1}, {a2, b2}, {a3, b3}};
MyBimap map(v.begin(), v.end());
It also didn't work. The documentation isn't exactly clear what kind of iterators this constructor expects, but apparently it's not simply an iterator of std::pair<A, B> objects. Then what does this constructor expect for this kind of bimap?
I use the following "factory function" that takes a braced initializer list and returns a boost::bimap:
template <typename L, typename R>
boost::bimap<L, R>
make_bimap(std::initializer_list<typename boost::bimap<L, R>::value_type> list)
{
return boost::bimap<L, R>(list.begin(), list.end());
}
Usage:
auto my_bimap = make_bimap<int, int>({{1, 2}, {3, 4}, {5, 6}});
C++ beginner here: You can use boost::assign to generate the initialization. I found this solution here.
Example:
#include <boost/bimap.hpp>
#include <boost/assign.hpp>
//declare the type of bimap we want
typedef boost::bimap<int, std::string> bimapType;
//init our bimap
bimapType bimap = boost::assign::list_of< bimapType::relation >
( 1, "one" )
( 2, "two" )
( 3, "three" );
//test if everything works
int main(int argc, char **argv)
{
std::cout << bimap.left.find(1)->second << std::endl;
std::cout << bimap.left.find(2)->second << std::endl;
std::cout << bimap.left.find(3)->second << std::endl;
std::cout << bimap.right.find("one")->second << std::endl;
std::cout << bimap.right.find("two")->second << std::endl;
std::cout << bimap.right.find("three")->second << std::endl;
/* Output:
* one
* two
* three
* 1
* 2
* 3
*/
}
The iterator begin/end should be for a sequence of bimap values.
boost::bimap< A, B>::value_type
A bimap value is a lot like a std::pair and can be initialized with {a1, b1}syntax. A vector of them seems to work too, which provides usable iterators for the constructor.
Ok, here is an example that compiles and runs for me (gcc 4.8.2 --std=c++11)
#include <vector>
#include <boost/bimap.hpp>
using namespace std;
int main() {
typedef boost::bimap< int, int > MyBimap;
std::vector<MyBimap::value_type > v{{1, 2}, {3, 4}, {5, 6}};
MyBimap M(v.begin(),v.end());
std::cout << "The size is " << M.size()
<< std::endl;
std::cout << "An entry is 1:" << M.left.at(1)
<< std::endl;
}
This leaves a vector to be cleaned up, which might in some cases be an issue. Here a short helper class that might solve your problem as well. As the class instance is a temporary, it gets cleaned up immediately wherever it is used. This is based on https://stackoverflow.com/a/1730798/3103767
// helper for bimap init (simple, lightweight version of boost::assign)
template <typename T, typename U>
class create_bimap
{
typedef boost::bimap< T, U > bimap_type;
typedef typename bimap_type::value_type value_type;
private:
boost::bimap<T, U> m_map;
public:
create_bimap(const T& left, const U& right)
{
m_map.insert( value_type(left, right) );
}
create_bimap<T, U>& operator()(const T& left, const U& right)
{
m_map.insert( value_type(left, right) );
return *this;
}
operator boost::bimap<T, U>()
{
return m_map;
}
};
Use as follows:
boost::bimap<string,int> myMap = create_bimap<string,int>
("c",1)
("b",2)
("a",3);

Alternate between vectors and arrays using templates

Let's assume that I have a function that prints a set of numbers: 1, 2, 3, 4, 5 and these numbers can either be stored as an array, or, as a vector. In my current system I therefore have two functions that accept either of these parameters.
void printNumbers(std::vector<double> &printNumbers)
{
//code
//....
}
And therefore one that accepts an array..
void printNumbers(int* numbers)
{
//code
//...
}
This seems a waste of code, and, I was thinking that I could better take advantage of code re-use which got me thinking to this: Can I use a template to determine which type of input is being passed to the function? For example, whether it's a vector or an array or just a single integer value?
Here is the prototype below:
#include <iostream>
using namespace std;
template<class T>
void printNumbers(T numbers)
{
// code
// code
}
int main(int argc, char *argv[]) {
int numbers[] = {1, 2, 3, 4, 5};
printNumbers<array> (numbers);
}
Any help would be greatly appreciated.
The usual idiom is to pass iterators, one for the first element of the range, and one corresponding to "one past the end":
template<class Iterator>
void printNumbers(Iterator begin, Iterator end)
{
for (Iterator i = begin; i != end; ++i)
std::cout << *i << " ";
std::cout << "\n";
}
int main()
{
int numbers[] = {1, 2, 3, 4, 5};
printNumbers(numbers, numbers + 5);
printNumbers(std::begin(numbers), std::end(numbers); // C++11 version
std::vector<int> v{1,2,3,4,5};
printNumbers(v.begin(), v.end());
}
You could follow the example of the STL algorithms and accept an iterator range. Containers have their iterator types, and pointers can be used to iterate over arrays:
template <typename InputIterator>
void printNumbers(InputIterator start, InputIterator end) {
// print "*start", and iterate up to "end"
}
For convenience, you can overload this to accept containers and arrays directly:
template <typename Container>
void printNumbers(Container const & c) {
printNumbers(c.begin(), c.end());
}
template <typename T, size_t N>
void printNumbers(T (const & a)[N]) {
printNumbers(a, a+N);
}
In C++11 (or with your own begin and end functions) you can combine these:
template <typename Container>
void printNumbers(Container const & c) {
printNumbers(std::begin(c), std::end(c));
}