Sign in to follow this  
rozz666

[C++] SFINAE

Recommended Posts

I don't understand why this code doesn't compile. The error is with std::iterator_traits. Compiler says it can't find iterator_category for std::vector which is fine. Therfore the second template should be excluded from matching. Somehow, it casues an error. Any ideas? Don't write that it's not useful. I know it. I'm only testing.
#include <vector>
#include <algorithm>
#include <iterator>

template <typename Cont, typename Cont2>
typename Cont2::iterator copy(const Cont& c, Cont2& dest)
{
    //return std::copy(c.begin(), c.end(), std::back_inserter(dest));
    std::copy(c.begin(), c.end(), std::back_inserter(dest));
    return dest.end();
}

template <typename Cont, typename It>
It copy(const Cont& c, It dest, typename std::iterator_traits<It>::pointer = 0)
{
    return std::copy(c.begin(), c.end(), dest);
}

int main()
{
    std::vector<int> a, b;

    copy(a, b);
}



Comeau errors:
"stl_iterator_base.h", line 110: error: class
          "std::vector<int, std::allocator<int>>" has no member
          "iterator_category"
    typedef typename _Iterator::iterator_category iterator_category;
                                ^
          detected during instantiation of class
                    "std::iterator_traits<_Iterator> [with
                    _Iterator=std::vector<int, std::allocator<int>>]" at line
                    21 of "ComeauTest.c"

"ComeauTest.c", line 21: error: more than one instance of overloaded function "copy"
          matches the argument list, the choices that match are:
            function template "Cont2::iterator copy(const Cont &, Cont2 &)"
            function template "It copy(const Cont &, It,
                      std::iterator_traits<_ForwardIter2>::pointer)"
            The argument types that you used are: (std::vector<int, std::allocator<int>>,
                      std::vector<int, std::allocator<int>>)
      copy(a, b);
      ^
[Edited by - rozz666 on May 29, 2009 7:11:36 AM]

Share this post


Link to post
Share on other sites
I get similar error messages from VC++, but I think they may be red herrings and the real culprit may be:
line 21
error C2668: 'copy' : ambiguous call to overloaded function


Additionally, neither function is a viable candidate for instantiation

template <typename Cont, typename Cont2>
typename Cont2::iterator copy(const Cont& c, Cont2& dest)
{
return std::copy(c.begin(), c.end(), std::back_inserter(dest));
}

causes

error C2664: 'std::_Vector_iterator<_Ty,_Alloc>::_Vector_iterator(const std::_Vector_iterator<_Ty,_Alloc> &)' : 
cannot convert parameter 1 from 'std::back_insert_iterator<_Container>' to 'const std::_Vector_iterator<_Ty,_Alloc> &'

while

template <typename Cont, typename It>
It copy(const Cont& c, It dest, typename std::iterator_traits<It>::pointer = 0)
{
return std::copy(c.begin(), c.end(), dest);
}

causes

error C2039: 'iterator_category' : is not a member of 'std::vector<_Ty>'



HTH

A5

Share this post


Link to post
Share on other sites
The problem seems to be that you are not just checking whether It has a subtype pointer, but trying to instantiate the iterator_traits template which fails because vector doesn't have iterator_category typedef. I don't know if that should be covered by SFINAE.

A solution might be to check for a type that is typedeffed in iterator types directly, and provide a third overload for pointers.


#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

template <typename Cont, typename Cont2>
typename Cont2::iterator copy(const Cont& c, Cont2& dest)
{
std::cout << "Container\n";
std::copy(c.begin(), c.end(), std::back_inserter(dest));
return dest.end();
}

template <typename Cont, typename It>
It copy(const Cont& c, It dest, typename It::iterator_category* = 0)
{
std::cout << "Iterator\n";
return std::copy(c.begin(), c.end(), dest);
}

template <typename Cont, typename Pointer>
Pointer* copy(const Cont& c, Pointer* p)
{
std::cout << "Pointer\n";
return std::copy(c.begin(), c.end(), p);
}

int main()
{
std::vector<int> a, b;
std::vector<int>::iterator it;
int* p;

copy(a, b);
copy(a, it);
copy(a, p);
}



(Or you could just rename one of the overloads - e.g the first might be called append instead - otherwise, if you indeed wanted to copy, you could use vector assignment operator. What kind of a system would it be where you don't know whether you'll end up with an iterator or a container where it would be important for them to be callable in the same way?)

Share this post


Link to post
Share on other sites
Quote:
Original post by ApochPiQ
Quote:
Original post by rozz666
*bump*


Please do not bump posts in this forum.


Thanks! [smile]


I am not a big friend of bumping (that is, when the bump frequency is higher than 1 bump in 3 days), too, but I think I have the slight memory of another moderator "allowing" it.

Evil Steve, if you hear me, it was not by accident you? Otherwise, all my apologies :|

I fail to find the relevant post right now, though.

Share this post


Link to post
Share on other sites
@phrensel: Help Wanted allows bumping once every 24 hours. In comparison, bumping once every 1-2 hours here is entirely inappropriate. It's still in the most recent 5-10 threads at that point. The idea behind bumping is to get things visible in the forum once more, and even then, expanding on your question is almost always much preferred -- if you're bumping, presumably there's a reason people aren't answering your question, right?

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
You may not have noticed but ApochPiQ is now the moderator for General Programming. He is now the guy who determines policy here.


Then it was a misunderstanding from my side about "this forum" meaning "general programming forum" and not "gamedev.net forum(s)". Though I think it can be confusing to have different policies for each forum, but that probably has reasons; otoh my question is now answered and for the rest I don't really care as I never bump :D


Quote:
Original post by MaulingMonkey
@phrensel: Help Wanted allows bumping once every 24 hours. In comparison, bumping once every 1-2 hours here is entirely inappropriate. It's still in the most recent 5-10 threads at that point. The idea behind bumping is to get things visible in the forum once more, and even then, expanding on your question is almost always much preferred -- if you're bumping, presumably there's a reason people aren't answering your question, right?


Yes, full agree. As I said, I don't care as long as the bump frequency is a bump every 3 days or so.

Share this post


Link to post
Share on other sites
Quote:
Original post by phresnel
Then it was a misunderstanding from my side about "this forum" meaning "general programming forum" and not "gamedev.net forum(s)". Though I think it can be confusing to have different policies for each forum, but that probably has reasons; otoh my question is now answered and for the rest I don't really care as I never bump :D


Yeah, I know it gets hard to keep track of who allows what in which fora. That's why I posted a friendly message in the thread instead of bringing out the banhammer [wink]

Share this post


Link to post
Share on other sites
Quote:
Original post by rozz666
*bump*


You don't need to bump your thread every two hours. Even in General Programming, a thread will usually stay on the first page for a day or so. At the time of your bumpings, most users in North America (which account for a lot of the user base, as you can imagine) would have been asleep.

Share this post


Link to post
Share on other sites
Or you can just disable the second overload if It has a iterator typedef, which should be enough to lever ambiguity.

BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator)

template <typename Cont, typename Cont2>
typename Cont2::iterator copy(const Cont& c, Cont2& dest)
{
//return std::copy(c.begin(), c.end(), std::back_inserter(dest));
std::copy(c.begin(), c.end(), std::back_inserter(dest));
return dest.end();
}

template <typename Cont, typename It>
typename boost::disable_if<
has_iterator<It>,
It
>::type
copy(const Cont& c, It dest)
{
return std::copy(c.begin(), c.end(), dest);
}


In case you don't want to use boost enable_if or boost.mpl has_xxx, you can simply rewrite them.
has_xxx requires a trick or two though.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this