определение типа аргумента функции (контейнер std, например, вектор) не выполняется при использовании enable_if и SFINAE

Кажется, я не могу понять, в чем я ошибаюсь. см. https://ideone.com/WKsZSN

Я пытаюсь создать функцию, которая существует только в том случае, если ее аргумент является определенным типом шаблонного класса, предоставляющего typedef для итератора.

В безусловном случае функция выглядела бы примерно так:

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

в этом случае вывод типа отлично работает для этого фрагмента:

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

Ok. так что теперь я хочу ввести -deduce мои аргументы и enable_if, если класс контейнера предоставляет итератор typedef. используя паттерн herb sutter gotw sfinae, я создал:

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);
};

Хорошо, поэтому, используя это, я теперь могу определить, открыт ли итератор:

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

отлично работает и выводит:

did something!
vector<int> supports_iteration? true!

хорошо, теперь я хочу обновить DoSomething (), используя enable_if следующим образом:

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";
}

но это не работает. я получил

prog.cpp: В функции ‘int main ()’: prog.cpp: 44: 22: error: нет соответствующей функции для вызова DoSomethingSmartly (std :: vector &) ’DoSomethingSmartly (v); // - не работает !! ^ prog.cpp: 26: 6: примечание: кандидат: класс шаблона C, класс T, класс A> void DoSomethingSmartly (typename std :: enable_if> :: value> :: type &) void DoSomethingSmartly (^ ~~~~~~ ~~~~~~~~~~~ prog.cpp: 26: 6: примечание: ошибка вывода / подстановки аргумента шаблона: prog.cpp: 44: 22: примечание: не удалось вывести параметр шаблона 'шаблонный класс C' DoSomethingSmartly (v); // - не работает !!

Что я делаю неправильно?


person chrisg    schedule 21.04.2017    source источник


Ответы (3)


В вашей попытке C, T, A находятся в невыводимом контексте (в traits<T>::type, T в невыводимом контексте), вы можете использовать enable_if для возвращаемого типа:

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)
{
   // ...
}
person Jarod42    schedule 21.04.2017
comment
невыводимый контекст. интересно. я займусь этим. Благодарю. - person chrisg; 21.04.2017

@ Jarod42 дал правильный ответ в своем комментарии, но я добавлю это в условиях непрофессионала:

При рассмотрении просто ...

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

... компилятор не может вывести тип C, T, A из аргумента вектора, потому что C<T,A> в support_iteration<C<T,A>>::value находится в невыводимом контексте.

Этот ответ объясняет это более подробно.

Следующее изменение исправит это:

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";
    }

Теперь первый аргумент используется для вывода C, T, A, а второй аргумент используется для определения возможности вызова функции на основе SFINAE. * = 0 используется, чтобы вам никогда не приходилось передавать дополнительный параметр.

person Werner Erasmus    schedule 21.04.2017

Я понял. то, что я действительно хотел, было это (на самом деле меня не волновала итерация, это был плохой прокси для раскрытия синтаксической функции T :: size ()):

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";
}

... но я все еще хочу знать, почему определение типа не удалось !!!

person chrisg    schedule 21.04.2017
comment
В traits<T>::type, T в невыводимом контексте. - person Jarod42; 21.04.2017