0
votes

I can't seem to figure out where I'm going wrong. see https://ideone.com/WKsZSN

I am trying to create a function that exists only if its argument is a certain kind of templated class exposing a typedef for iterator.

In the non-conditional case, the function would look something like this:

template<template <class, class> class C, class T, class A>
void DoSomething(C<T,A>& val)
{
    T* pT;
    cout << "did something!\n";
}

in this case type-deduction works fine for this snippet:

vector<int> v{1,2,3,4,5};
DoSomething(v);

ok. so now I want to type -deduce my arguments and enable_if the container class exposes typedef iterator. using herb sutter gotw sfinae pattern, I created:

template<class T> struct supports_iteration
{ 
private:
    typedef char yes[1];
    typedef char no[2];
    template <class C> static yes& foo(typename C::iterator*);
    template <class C> static no& foo(...);
public:
    static constexpr bool value = sizeof(foo<T>(0)) == sizeof(yes);
};

ok, so using this, I can now detect if iterator is exposed:

vector<int> v{1,2,3,4,5};
DoSomething(v);
cout << "vector<int> supports_iteration? " << 
    boolalpha << supports_iteration<decltype(v)>::value << "!" << endl;

works fine and outputs:

did something!
vector<int> supports_iteration? true!

ok, now i want to upgrade DoSomething() using enable_if like this:

template<template <class, class> class C, class T, class A>
void DoSomethingSmartly(
    typename std::enable_if<
        supports_iteration<
            C<T,A>
        >::value
    >::type& val)
{
    T* pT;
    cout << "did something smartly!\n";
}

but this doesn't work. I get

prog.cpp: In function ‘int main()’: prog.cpp:44:22: error: no matching function for call to ‘DoSomethingSmartly(std::vector&)’ DoSomethingSmartly(v);// - fails!! ^ prog.cpp:26:6: note: candidate: template class C, class T, class A> void DoSomethingSmartly(typename std::enable_if >::value>::type&) void DoSomethingSmartly( ^~~~~~~~~~~~~~~~~~ prog.cpp:26:6: note: template argument deduction/substitution failed: prog.cpp:44:22: note: couldn't deduce template parameter ‘template class C’ DoSomethingSmartly(v);// - fails!!

what am I doing wrong?

3

3 Answers

2
votes

In your attempt, C, T, A are in non deducible context (In traits<T>::type, T in in a non deducible context), you might use enable_if on return type:

template<template <class, class> class C, class T, class A>
typename std::enable_if<supports_iteration<C<T,A>>::value>::type
DoSomethingSmartly(C<T, A>& val)
{
   // ...
}
1
votes

@Jarod42 has given the correct answer in his comment, but I'll add this in layman's terms:

When considering just...

template<template <class, class> class C, class T, class A>
void DoSomethingSmartly(
    typename std::enable_if<
      supports_iteration<C<T,A>>::value>::type&);

... the compiler can't deduce the type of C, T, A from the vector argument, because C<T,A> in support_iteration<C<T,A>>::value is in a non-deducible context.

This answer explains it in more detail.

The following change would fix this:

template<template <class, class> class C, class T, class A>
    void DoSomethingSmartly(
        C<T,A>& c, //Now deducible...
        typename std::enable_if<supports_iteration<C<T,A>>::value>::type* = 0)
    {
        T* pT;
        cout << "did something smartly!\n";
    }

Now the first argument is used to deduce C, T, A and the second argument is used to determine whether the function is callable based on SFINAE. * = 0 is used so that you never have to pass the additional parameter in.

0
votes

I figured it out. what I really wanted was this (I actually didn't care about iteration, it was a bad proxy for exposing syntactic T::size() function):

template<template <class, class> class C, class T, class A, 
    typename = decltype(
        declval<C<T,A>>().size()
        ,void()
    )
>
void DoSomethingReallySmartly(C<T,A>& val)
{
    T* pT;
    cout << "did something really smartly!\n";
}

...but I still want to know why the type deduction failed in the original attempt!!!