std :: string не работает с std :: set - PullRequest
1 голос
/ 08 апреля 2011

Я делаю вопрос по программированию из C ++ Primer Plus, который просит меня сделать шаблон
функция, которая возвращает количество уникальных элементов в массиве. Я не понимаю, почему
Строка 13 вызывает ошибку при компиляции, насколько мне известно, std :: string ведет себя как массив.

Это мой код:

#include <iostream>
#include <set>

template <typename T>
int reduce(T ar[], int n);

int main()
{
    long test[] = {1, 2, 1, 3, 3, 4, 1};
    std::string testStr = "testing";

    std::cout << reduce(test, 6) << std::endl;
    std::cout << reduce(testStr, 7) << std::endl;

    std::cin.get();

    return 0;
}

template <typename T>
int reduce(T ar[], int n)
{
    std::set<T> test;
    for(int i = 0; i < n; i++)
    {
        test.insert(ar[i]);
    }
    return test.size();
}

Ответы [ 3 ]

4 голосов
/ 08 апреля 2011

После моего немедленного ответа, что std::string не является массивом, именно так C ++ может выполнить задачу, которую вы ищете.

#include <iterator>
#include <iostream>
#include <set>

// instead of taking an array and length, just take where you want to start and where
// you want to stop.
template <typename TForwardIterator>
int reduce(TForwardIterator iter, TForwardIterator end)
{
    // This is hideous syntax to get the type of object the iterator is describing.
    // For std::string, it is char...for T*, it is T.
    // I apologize for C++, I'm not sure there is a better way to do this.
    typedef typename std::iterator_traits<TForwardIterator>::value_type value_type;
    std::set<value_type> set;

    // instead of forcing the objects to be an array type, use iterators!
    for (; iter != end; ++iter)
        set.insert(*iter);
    return set.size();
}

int main()
{
    long test[] = {1, 2, 1, 3, 3, 4, 1};
    std::string testStr = "testing";

    // begin() and end() are iterators you'll find on all the container types
    std::cout << reduce(testStr.begin(), testStr.end()) << std::endl;
    // pointers are iterators, too!
    std::cout << reduce(test, test + 7) << std::endl;

    return 0;
}
2 голосов
/ 08 апреля 2011

Ответ довольно прост: std::string не является массивом.

Он ведет себя как массив, поскольку вы можете получить доступ к элементам с помощью оператора [], но это просто не тот тип данных, что char[]. На самом деле стандарт даже не гарантирует, что он хранится как массив (имеется в виду непрерывно). T[] будет соответствовать только массиву, но не объектам, которые могут быть использованы как массивы.

Для решения этой проблемы у вас есть несколько вариантов

  1. Вы можете вызвать reduce(teststr.c_str(), 7), так как c_str() вернет chararray с содержимым строки.
  2. Вы можете переписать reduce как template <typename T, typename U> int reduce(U ar, int n) и назвать его как reduce<long>(test, 6) и reduce<char>(testStr, 7). Второй параметр шаблона необходим, поскольку не существует единого способа получить из контейнера элемент (кроме C ++ 0x / с использованием расширений компилятора).
  3. Если вы используете c ++ 0x, вы можете использовать decltype для перехода из контейнера к содержащемуся элементу: template <typename T>int reduce(T ar, int n) и std::set<decltype(ar[0])> test; (остальная часть кода остается неизменной, и у меня почему-то возникают проблемы с блоком кода разделы, так что только эти две строки здесь.

Конечно, в c ++ можно было бы написать такую ​​функцию в терминах итераторов (см. Ответ Трэвиса Гокельса), поскольку это просто более гибкий и лучше поддерживаемый способ.

1 голос
/ 08 апреля 2011

Возможно, вы путаете std :: strings со встроенными символьными массивами.std :: strings не являются массивами, хотя ведут себя аналогично массивам (у класса есть перегруженный оператор []) и содержат массивы (к которым вы можете получить доступ через c_str ()).

Если вы замените строку 10 на

char testStr[] = "testing";

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

Или вы можете попробовать что-то вроде:

#include <iostream>
#include <set>

template <typename T>
int reduce(const T* ar, int n);

int main()
{
    long test[] = {1, 2, 1, 3, 3, 4, 1};
    std::string testStr = "testing";

    std::cout << reduce(test, 7) << std::endl;
    std::cout << reduce(testStr.c_str(), testStr.size()) << std::endl;

    std::cin.get();

    return 0;
}

template <typename T>
int reduce (const T* ar, int n)
{
    std::set<T> test;
    for(int i = 0; i < n; i++)
    {
        test.insert(ar[i]);
    }
    return test.size();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...