Не могу определить тип шаблона

Я пытаюсь передать итератор в качестве параметра шаблона методу шаблона, но компилятор жалуется, что:

error C2783: 'void Test::Assert(std::vector<T>::const_iterator)':
could not deduce template argument for 'T'

Код, вызывающий ошибку:

#include "stdafx.h"
#include <iostream>
#include <vector>

class Test
{
    public:
        template <typename T>
        void Assert(typename std::vector<T>::const_iterator it)
        {
            std::cout << *it << std::endl;
        }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Test test;

    std::vector<double> myVec;

    test.Assert(myVec.cbegin());

    return 0;
}

Я предполагаю, что есть простой способ сделать эту работу, поскольку большинство алгоритмов std могут выводить тип из итератора.


person Q-bertsuit    schedule 16.04.2015    source источник
comment
возможный дубликат обходного пути для невыведенного контекста   -  person vsoftco    schedule 16.04.2015


Ответы (5)


Причина в том, что форма, в которой вы используете T, является невыведенным контекстом:

template <typename T>
void Assert(typename std::vector<T>::const_iterator it)

Рассмотрим более простой случай, чтобы понять, почему:

struct A { using type = int; };
struct B { using type = int; };
struct C { using type = int; };

template <typename T>
void Assert(typename T::type it) { ... }

Assert(5);

Что надо T выводить как? Определить невозможно. Вам нужно будет явно указать тип ... как что-то вроде Assert<A>(5).

См. Также Что такое невыведенный контекст?

поскольку большинство алгоритмов std могут выводить тип из итератора.

Это потому, что стандартные алгоритмы просто определяют тип итератора, а не типа контейнера. Например, std::find просто:

template <class InputIt, class T>
InputIt find( InputIt first, InputIt last, const T& value );

Здесь вообще нет понятия «контейнер» - нужно вывести только тип итератора. Это часть красоты библиотеки алгоритмов.

Итак, если вы хотите просто вывести содержимое итератора, правильной функцией будет просто:

template <typename Iterator>
void Assert(Iterator it)
{
    std::cout << *it << std::endl;
}

Когда вы вызываете Assert(myVec.cbegin()), Iterator будет выведено как std::vector<double>::const_iterator, что именно то, что вы хотите.

person Barry    schedule 16.04.2015
comment
Понравилось, как вы это объяснили - person Ajay; 16.04.2015

Стандартные алгоритмы выглядят так:

template <typename Iterator>
void some_algorithm(Iterator first, Iterator last) {
  // do stuff
}

Если им нужен тип итератора, они могут использовать typename std::iterator_traits<Iterator>::value_type.

Они никоим образом не ссылаются на контейнер, такой как vector. Не все итераторы происходят из контейнеров.

person Sebastian Redl    schedule 16.04.2015
comment
было бы неплохо показать, как это будет выглядеть на этом примере. - person Alexander Oh; 16.04.2015

template <typename Ite>
void Assert(Ite &&it)
{
    std::cout << *std::forward<It>(it) << std::endl;
}

Вот и все - стандартная библиотека просто параметризует весь тип итератора. Фактически, можно использовать все, что ведет себя как итератор (это основная причина, по которой итераторы ведут себя как указатели). Это называется «утиная печать».

То, что вы пытаетесь сделать (ограничение функции только теми типами, которые являются явными итераторами), - это то, о чем говорят концепции C ++ 17.

person Quentin    schedule 16.04.2015

#include "stdafx.h"
#include <iostream>
#include <vector>

class Test
{
 public:
    template <typename T>
    void Assert(typename T::const_iterator it)
    {
        std::cout << *it << std::endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
   Test test;

   std::vector<double> myVec;

   test.Assert<std::vector<double> >(myVec.cbegin());

   return 0;
}

Попробуйте это один раз.

person Launa    schedule 16.04.2015
comment
С некоторыми значениями не с пустым вектором - person Launa; 16.04.2015

Следующий код скомпилирован с использованием clang.

#include <iostream>
#include <vector>

class Test
{
    public:
        template <typename T>
        void Assert(typename std::vector<T>::const_iterator it)
        {
            std::cout << *it << std::endl;
        }
};

int main(int argc, char* argv[])
{
    Test test;

    std::vector<double> myVec;

    myVec.push_back(2.0f);

    test.Assert<double>(myVec.cbegin());  // call Assert in this way.

    return 0;
}

Выходы:

$ ./a.out
2

Версия компилятора:

$ clang++ -v
Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM
3.6.0svn) Target: x86_64-apple-darwin14.1.0 Thread model: posix
person douyw    schedule 16.04.2015