# Problem using nested std::pairs

This topic is 2651 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I'm trying to compile code that calls a function which returns a std::pair<std::pair<Iterator2d,Iterator2d>,bool> object. The function being called is itself a template function:

template<class Iter2d>
std::pair<std::pair<Iter2d, Iter2d>,bool> Math2D::getDiagonalAndConvexity(Iter2d begin, Iter2d end, bool ccwInside)

This function compiles fine but when I try to call it with the following code, I get an error

void PlaneVertices::evalDiagonalAndConvexity()
typedef std::pair<Iterator2d,Iterator2d> IterPair;
std::pair<IterPair,bool> ret = Math2D::getDiagonalAndConvexity(begin(),end(),Math2D::get2DWinding(begin(),end()));
}

The line with the function call is producing the following errors:

Error 4 error C2664: 'std::pair<_Ty1,_Ty2>::pair(const std::pair<_Ty1,_Ty2> &)' : cannot convert parameter 1 from 'const PlaneVertices::Iterator2d' to 'const std::pair<_Ty1,_Ty2> &' c:\program files (x86)\microsoft visual studio 9.0\vc\include\utility 57
Error 5 error C2440: 'initializing' : cannot convert from 'const PlaneVertices::Iterator2d' to 'bool' c:\program files (x86)\microsoft visual studio 9.0\vc\include\utility 57
Error 6 error C2439: 'std::pair<_Ty1,_Ty2>::second' : member could not be initialized c:\program files (x86)\microsoft visual studio 9.0\vc\include\utility 57

What am I doing wrong?

##### Share on other sites
Let us first make it readable using source/code-tags and different formatting:

template<class Iter2d>std::pair<    std::pair<        Iter2d,         Iter2d>,    bool> Math2D::getDiagonalAndConvexity(Iter2d begin, Iter2d end, bool ccwInside)

void PlaneVertices::evalDiagonalAndConvexity()	typedef std::pair<Iterator2d,Iterator2d> IterPair;	std::pair<IterPair,bool> ret =              Math2D::getDiagonalAndConvexity(                   begin(),                   end(),                   Math2D::get2DWinding(begin(),end())             );}

cannot convert parameter 1 from     'const PlaneVertices::Iterator2d' to     'const std::pair<_Ty1,_Ty2> &'

##### Share on other sites
I think we need more context:
* Full function definitions of all involved functions
* Definition of PlaneVertices::Iterator2d

##### Share on other sites
Iterator2d is a template instantiation of Proj2dVecIter<VertexType>. Proj2dVecIter is a custom iterator constructed with boost::iterator_facade. It's purpose is to iterate over 3D sets of coordinates, treating them as 2D. Most operations of 3D planar objects can be similified by treating as 2D, which is done by ignoring one of the x,y or z components.

begin() and end() return Iterator2d objects which refer to the first in the set, and the element beyond the last in the set, respectively.

I should point out that the errors only occur when *nested* std::pairs are used. I was previously returning std::pair<Iterator2d, Iterator2d> from the function with no problem at all. Then I added one little bool and the calling code stopped working.

Definitions for begin() and end()
PlaneVertices::Iterator2d PlaneVertices::begin() const{	const Vec3& begin3D = vertexStrip[0];	return Iterator2d(&begin3D[iX], &begin3D[iY]);}PlaneVertices::Iterator2d PlaneVertices::end() const{	const Vec3& begin3D = vertexStrip[0];	std::size_t numBytes = vertexStrip.size() * sizeof(TexturedVertex);	return Iterator2d((float*)((char*)&begin3D[iX] + numBytes), (float*)((char*)&begin3D[iY] + numBytes));}

Definition for Proj2dVecIter
// 2D projection iterator - Generates 2D projection coords from vectors of objects with 3D coord datatemplate<class VertexFormat>class Proj2dVecIter : public boost::iterator_facade<Proj2dVecIter<VertexFormat>, VertexFormat const, boost::random_access_traversal_tag, Vec2>{public:	Proj2dVecIter() {}	Proj2dVecIter(const float* pProjX, const float* pProjY) : pProjX(pProjX), pProjY(pProjY) {}private:	friend class boost::iterator_core_access;	void increment(){		pProjX = (float*)((char*)pProjX + sizeof(VertexFormat));		pProjY = (float*)((char*)pProjY + sizeof(VertexFormat));	}	void decrement(){		pProjX = (float*)((char*)pProjX - sizeof(VertexFormat));		pProjY = (float*)((char*)pProjY - sizeof(VertexFormat));	}	bool equal(const Proj2dVecIter& rhs) const{		return rhs.pProjX == pProjX;	}	Vec2 dereference() const{		return Vec2(*pProjX,*pProjY);	}	void advance(std::ptrdiff_t d){		pProjX = (float*)((char*)pProjX + sizeof(VertexFormat) * d);		pProjY = (float*)((char*)pProjY + sizeof(VertexFormat) * d);	}	std::ptrdiff_t distance_to(const Proj2dVecIter& rhs) const{		return (VertexFormat*)rhs.pProjX - (VertexFormat*)pProjX;	}	const float* pProjX, *pProjY;};

Again, there are no errors present when std::pair objects are not nested.

##### Share on other sites
So, I'm sorry, but I don't have time to debug templates today. So I'm not gonna help you there.

However, one thing to consider, do you /really/ need this code? To the average person looking at it, it simply isn't clear what /kind/ of thing a
pair<pair<iterator,iterator>,bool>
is supposed to be. From my experience, std::pair is useful when you have a simple tuple that can only be described as some kind of tuple. For example, std::map<>::iterator is a tuple because an element in a map is a pair of two things: a key and a value. There isn't any better descriptive name for it (KeyValueTuple? FindReturnType? MapElementAccessor?). It is relatively simple, and pair<Key,Value> is an exact description of /what/ it represents.

On the other hand, a mathematical 2D point /could/ be represented as pair<float,float>. However, almost no-one does this. Why? Because, in this case, even though it is also simple, pair<float,float> actually obscures the meaning of what the datatype is supposed to represent. Even though it is more typing and more space and less standard,
struct Point2D { float x,y;};
is a THOUSAND times more understandable and makes for better code.

Of course, as we add nested pairs to represent complex data structures, this gets worse. What if we want a 2d circle? It's a center point and a radius: pair<pair<float,float>,float> ? How about "struct Circle2D { Point2D center; float radius; };" instead. What if we want a 3d point as well? pair<pair<float,float>,float> ? how is this different from a circle? The types are identical.

What about a sphere? (Oh god) pair<pair<pair<float,float>,float>,float>.

My point is that creating data structures this way is an inherently bad idea. Although this doesn't fix your compile problem, here are some alternate suggestions

//suggestion 1template<class Iter2d>struct Diagonal{Iter2d d1,d2;};template<class Iter2d>struct DiagonalAndConvexityResult{Diagonal<Iter2d> diagonal;bool is_convex;};template<class Iter2d>DiagonalAndConvexityResult<Iter2d> Math2D::getDiagonalAndConvexity(Iter2d begin,Iter2d end,bool ccwInside);//suggestion 2template<class Iter2d>struct DiagonalAndConvexityResult{Iter2d d1,d2;bool is_convex;};template<class Iter2d>DiagonalAndConvexityResult<Iter2d> Math2D::getDiagonalAndConvexity(Iter2d begin,Iter2d end,bool ccwInside);

##### Share on other sites
Quote:
 However, one thing to consider, do you /really/ need this code?

No. I'll probably rewrite it to return a struct instead, which is a better idea now that there's 3 return values instead of just 2.
But I still want to get this problem solved. I don't like mysteries.

##### Share on other sites
A std::pair<IterPair, bool> is constructed from an IterPair and a bool.

You are trying to construct it from an iterator, an iterator and a bool.

That doesn't match the expected parameters: an iterator (parameter 1) is not an IterPair, an iterator (parameter 2) is not a bool, and then you have this extra bool hanging around.

Solution: explicitly construct the IterPair first:

std::pair<IterPair, bool> ret = Math2D::getDiagonalAndConvexity(std::make_pair(begin(), end()), Math2D::get2DWinding(begin(), end()));

##### Share on other sites
Quote:
 Solution: explicitly construct the IterPair first:std::pair ret = Math2D::getDiagonalAndConvexity(std::make_pair(begin(), end()), Math2D::get2DWinding(begin(), end()));

That isn't it. The function getDiagonalAndConvexity takes 3 parameters - typename, typename and bool. The parameters aren't wrapped in a std::pair, but the returned values, which are the same type as the parameters, are.

I erased part of the calling code and stil get the same error
void PlaneVertices::evalDiagonalAndConvexity(){	typedef std::pair<Iterator2d,Iterator2d> IterPair;	Math2D::getDiagonalAndConvexity(begin(),end(),Math2D::get2DWinding(begin(),end()));}

Iterator2d is created with Boost and utilises the "curiously recurring template pattern". Is it possible that this is confusing the compiler somehow?

##### Share on other sites
Post a minimal, but complete, code sample that demonstrates your problem.

##### Share on other sites
You have to tell the function what classes are involved. Iter2D in the function definition is just a placeholder for whatever class / struct you want to be used with said function.

Math2D::getDiagonalAndConvexity<TYPE_HERE>(                   begin(),                   end(),                   Math2D::get2DWinding(begin(),end())             );

or
Math2D<TYPE_HERE>::getDiagonalAndConvexity(                   begin(),                   end(),                   Math2D::get2DWinding(begin(),end())             );

Can't remember which way it goes, but Iter2D again is just a placeholder for the type that will be used (its the template not an actual class). So when you invoke the function you HAVE to tell it what Iter2D actually represents.

##### Share on other sites
Quote:
 You have to tell the function what classes are involved. Iter2D in the function definition is just a placeholder for whatever class / struct you want to be used with said function.

Didn't work. The first and second parameters are template types so the compiler is already able to resolve the correct instantiation automatically.

I'll try putting the code in a blank project and see if I can isolate the problem. I'm either missing something really obvious or there's an obscure limitation regarding templates that I'm not aware of.

##### Share on other sites
Since you're already using boost, why not replace the pair<,bool> with boost::optional?

##### Share on other sites
Solved it. Turns out the error was in the getDiagonalAndConvexity function, which utilised an early return statement which I had forgotten to update when I started using nested pairs. My fault for this taking so long- I never posted that part of the code for you guys to review.