Как я могу определить фактический тип переменной «auto»

В этом ответе:

https://stackoverflow.com/a/14382318/1676605

эта программа дается:

std::vector<int> vi{ 0, 2, 4 };
std::vector<std::string> vs{ "1", "3", "5", "7" };
for (auto i : redi::zip(vi, vs))
    std::cout << i.get<0>() << ' ' << i.get<1>() << ' ';

Я понятия не имею, что такое тип auto i, что затрудняет повторное использование опыта и обучение на примерах. Вот что возвращает замена auto i на char i

In function ‘int main()’:|
/data/cbworkspace/TestZip/TestZip.cpp|14|error: cannot convert ‘boost::iterator_facade<boost::zip_iterator<boost::tuples::tuple<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, __gnu_cxx::__normal_iterator<int*, std::vector<int> > > >, boost::tuples::cons<int&, boost::tuples::cons<int&, boost::tuples::null_type> >, boost::random_access_traversal_tag, boost::tuples::cons<int&, boost::tuples::cons<int&, boost::tuples::null_type> >, long int>::reference {aka boost::tuples::cons<int&, boost::tuples::cons<int&, boost::tuples::null_type> >}’ to ‘char’ in initialization|
/data/cbworkspace/TestZip/TestZip.cpp|14|warning: unused variable ‘i’ [-Wunused-variable]|
||=== Build finished: 1 errors, 1 warnings (0 minutes, 0 seconds) ===|

Попытайтесь выяснить тип из этого.

Есть ли способ определить тип переменной auto в C++11? Чтобы быть более ясным, у меня есть struct вот так:

struct EventData
{
    // return value from redi::zip<std::vector<PriceQuote>, std::vector<PriceQuote>> what goes here????? So REDI::Zip is zipping PriceQuote, PriceQuote of bids and asks.
};

struct PriceQuote
{
   double price;
   double size;
};

person user1676605    schedule 23.07.2013    source источник
comment
Проверьте ссылку, чтобы узнать, что возвращает redi::zip.   -  person Snps    schedule 23.07.2013
comment
(A) используйте IDE, (B) не используйте auto, если вы недостаточно знаете тип, чтобы его использовать   -  person Mooing Duck    schedule 23.07.2013
comment
Если вы не против получить ответ во время выполнения, вы всегда можете сделать что-то вроде std::cout << typeid(i).name() << std::endl;   -  person Borgleader    schedule 23.07.2013
comment
вы правы, но по крайней мере я могу заметить, что это какой-то итератор   -  person nio    schedule 23.07.2013
comment
@nio Диапазон, основанный на цикле for, фактически использует итераторы, предоставляемые либо бесплатной функцией, либо функцией-членом begin(), либо границами массива, но тип i будет тем, что возвращает оператор разыменования для этого итератора.   -  person Pixelchemist    schedule 23.07.2013
comment
Неважно, какой это тип, важно, как вы можете его использовать.   -  person Cat Plus Plus    schedule 24.07.2013
comment
@nio, это не итератор, это тип итератора reference, результат разыменования итератора   -  person Jonathan Wakely    schedule 24.07.2013
comment
Вам нужен возвращаемый тип redi::zip(vi, vs) или тип i в цикле? Они разные. Пожалуйста, уточните вопрос.   -  person Jonathan Wakely    schedule 24.07.2013
comment
Если вы все еще пишете typedef struct EventData { } EventData, вам действительно следует взять книгу по C++. Это по-прежнему разрешено для совместимости с C, но ваш код, очевидно, основан на C++.   -  person MSalters    schedule 29.07.2013
comment
Есть ли способ выяснить, что такое тип переменной auto? Да здесь!.   -  person PLG    schedule 20.09.2018


Ответы (8)


Почему вы хотите поместить этот тип в структуру? На самом деле он не предназначен для такого использования (я должен знать, я его написал!), но при необходимости вы можете использовать decltype и std::declval для определения типа (который все равно даст правильный ответ, если я изменю реализацию redi::zip)

struct EventData
{
  // type returned by redi::zip
  typedef decltype(redi::zip(std::declval<V1>(), std::declval<V2>())) zipper_type;

  // type referred to by zipper_type::iterator
  typedef std::iterator_traits<zipper_type::iterator>::value_type zipped_type;

  zipper_type m_zipper;
};

Н.Б. почему вы создаете typedef для struct? Это C++, а не C, хватит.

Я понятия не имею, что такое auto i, что затрудняет повторное использование опыта и обучение на примерах.

Привыкайте к этому. Вы знаете тип, который возвращает std::bind? Вы знаете тип, который возвращает std::mem_fn? Знаете ли вы, какой тип создает лямбда-выражение? Нет, вам не нужно знать, все, что вам нужно знать, это какие свойства у него есть и что вы можете с ним делать, а не то, как он называется или какие типы он содержит.

person Jonathan Wakely    schedule 23.07.2013
comment
Как я упоминал в другом комментарии, я не уверен, поможет ли это, поскольку redi::zip нужны исходные диапазоны, чтобы остаться в живых. - person Xeo; 24.07.2013
comment
Я не думаю, что ему действительно нужен диапазон, он, кажется, хочет тип от разыменования итератора в диапазон zip. - person Mooing Duck; 24.07.2013
comment
Кроме того, что касается std::bind и других ваших примеров, у нас есть std::function для их хранения именно по этой причине. - person Mooing Duck; 24.07.2013
comment
@MooingDuck, хм, возможно, вы правы насчет типа (хотя он говорит, что хочет возвращаемое значение из redi::zip, которое явно отличается от value_type этого типа) - ответ обновлен, чтобы дать этот тип. - person Jonathan Wakely; 24.07.2013
comment
@MooingDuck, да, точно, std::function работает с подходящими типами, когда вы знаете, что вы можете с этим сделать, а не точно, какой это тип. - person Jonathan Wakely; 24.07.2013
comment
@Jonathan: Ах, я недооценил это. Нвм тогда. :) - person Xeo; 24.07.2013
comment
@Xeo, я непреклонен в том, что любые диапазоноподобные типы, обсуждаемые комитетом ISO, не должны оставлять висячие ссылки при использовании с rvalue ... и redi::zip является доказательством концепции того, как сделать это правильно: ) - person Jonathan Wakely; 24.07.2013
comment
Причина в том, что я хочу передать событие другому потоку, и в этом потоке я хочу выполнить итерацию для извлечения заархивированных значений. Структура — это передаваемые данные, и она упаковывает redi::zip. - person user1676605; 24.07.2013
comment
@user1676605 user1676605, так что либо используйте decltype, либо вы можете сохранить сами контейнеры и выполнить архивирование в другом потоке. - person Jonathan Wakely; 24.07.2013
comment
Может ли кто-нибудь показать, как использовать std::function или decltype внутри структуры для извлечения значений? - person user1676605; 24.07.2013
comment
@user1676605 user1676605, вы не можете использовать std::function для этого - вы можете использовать std::function для переноса вызываемых типов (например, возвращаемых из std::bind и std::mem_fn), а не диапазоноподобных типов, возвращаемых redi::zip. - person Jonathan Wakely; 24.07.2013
comment
Если это предложение работает, это ИДЕАЛЬНО: @user1676605: В С++ 11 вы можете сделать именно это. Только с другим синтаксисом: decltype(redi::zip(vi,vs)) zip; - celtschk 38 минут назад - person user1676605; 24.07.2013
comment
@JonathanWakely Похоже, оператор auto может решить эту проблему с висячими диапазонами, а также с проблемой шаблона выражения. - person Casey; 24.07.2013
comment
@jonathan, когда я использую вашу структуру, я получаю эту ошибку: using iterator = decltype(std::begin(std::declval‹Container&›())); ../REDI/zip.h|38|ошибка: нет соответствующей функции для вызова ‘begin(PriceQuote&)’| PriceQuote — это V1, V2 в приведенной выше структуре. - person user1676605; 24.07.2013
comment
Есть ли у PriceQuote функция-член begin()? - person Jonathan Wakely; 24.07.2013
comment
@ Джонатан, нет, не так. С чего бы это, это просто структура данных? Посмотрите исходное сообщение о том, как выглядит PriceQuote. - person user1676605; 01.08.2013
comment
Я не знаю, почему это произойдет или нет, это вы пытаетесь заархивировать PriceQuote, вот что означает ошибка. Я полагаю, вы хотите заархивировать vector<PriceQuote>, но ошибка говорит о том, что вы этого не сделали. - person Jonathan Wakely; 01.08.2013

Попробуйте заменить auto на char и прочитать сообщение об ошибке.

person nio    schedule 23.07.2013
comment
ошибки может быть трудно читать, но это интересная мысль - person Mooing Duck; 23.07.2013
comment
особенно при использовании шаблонов! - person user1676605; 23.07.2013
comment
См. мой комментарий к Кейси выше - person user1676605; 23.07.2013
comment
передать его в template<class T>unsigned char identify(T&& v) {return v;}, который стирает бесполезный материал шаблона (если только он неявно конвертируется в беззнаковый символ...) - person Mooing Duck; 24.07.2013
comment
Это гениально. - person infinitezero; 18.06.2020

Вы бы нашли

for (boost::iterator_facade<
       boost::zip_iterator<
         boost::tuples::tuple<std::vector<int>::iterator,
                              std::vector<int>::iterator>
       >,
       boost::tuples::cons<int&, boost::tuples::cons<int&, boost::tuples::null_type> >,
       boost::random_access_traversal_tag,
       boost::tuples::cons<int&, boost::tuples::cons<int&, boost::tuples::null_type> >,
       long int
     >::reference i : redi::zip(vi, vs))
    std::cout << i.get<0>() << ' ' << i.get<1>() << ' ';

легче понять?

person Casey    schedule 23.07.2013
comment
Хороший вопрос, но тогда как мне присвоить возвращаемое значение redi::zip моей собственной переменной, не делая ее автоматической? typedef struct EventData { /*что здесь указать? */ } - person user1676605; 23.07.2013
comment
Как уже говорили выше, не важно, какой это тип, важно, что вы можете с ним сделать. Только из примера кода я знаю, что я могу сделать с ним итерацию в каком-то диапазоне boost::tuple<int, int>. Я бы использовал контейнер с указанными кортежами для хранения std::vector<boost::tuple<int,int>> и заполнил его boost::range::copy(redi::zip(vi, vs), myvector);. - person Casey; 23.07.2013
comment
Отлично, так что теперь вы добавляете копию в программу, которая не требует этого, чтобы вы могли ее сохранить? Смотрите мой измененный исходный вопрос. - person user1676605; 23.07.2013
comment
Ответ на вопрос, который вы процитировали, прямо гласит: см. ‹redi/zip.h› для функции zip, которая работает с range-base[d] для... поэтому, если это то, что вы действительно хотите: for(auto i: redi::zip(vi, vs)) myvector.push_back(i); сделает это. Я предпочитаю алгоритм явной копии. - person Casey; 23.07.2013
comment
Если вы просто хотите напрямую инициализировать контейнер, вы должны научиться использовать zip_iterator, который redi::zip упаковывает для вас в диапазон: boost.org/doc/libs/1_54_0/libs/iterator/doc/zip_iterator.html. - person Casey; 23.07.2013
comment
Я не ясно. Я хочу иметь переменную внутри структуры любого возвращаемого типа redi::zip без каких-либо потерь производительности и без необходимости разбираться в десятиуровневой тарабарщине шаблонов. Подобно этой typedef struct EventData{//return value from redi::zip что здесь происходит????? }Данные События; - person user1676605; 23.07.2013
comment
Как я сказал ниже, было бы здорово, если бы я мог сказать typedef struct EventData { независимо от redi::zip::returns zip; }Данные События; Поэтому я предлагаю новое ключевое слово С++ 11, что угодно или что угодно. Это похоже на auto, но определяет тип возвращаемого значения для меня во ВРЕМЯ КОМПИЛЯЦИИ без необходимости его назначения. Я не могу сказать auto i внутри структуры без присваивания? - person user1676605; 23.07.2013
comment
@user1676605: decltype хотел бы поговорить с вами. Однако это, скорее всего, вам не поможет - redi::zip возвращает ленивый диапазон, который зависит от живых исходных диапазонов. Он просто использует итераторы в этих исходных диапазонах, поэтому, если их нет, возвращаемый диапазон из redi::zip будет висеть. - person Xeo; 24.07.2013
comment
@Xeo: исходные диапазоны могут быть еще живы, мы не знаем, что это не так (если только конструкция redi::zip не создает временные диапазоны из векторов?) - person Mooing Duck; 24.07.2013
comment
redi::zip берет на себя ответственность за диапазоны rvalue (путем перемещения их в свои собственные переменные-члены) и содержит ссылки на диапазоны lvalue. Он не предназначен для хранения, он предназначен для немедленного создания и использования (например, в цикле for на основе диапазона), поэтому точка зрения Xeo в целом хорошая. - person Jonathan Wakely; 24.07.2013
comment
это, кажется, делает это: @user1676605: В С++ 11 вы можете сделать именно это. Только с другим синтаксисом: decltype(redi::zip(vi,vs)) zip; - celtschk 38 минут назад - person user1676605; 24.07.2013

Лучший способ выяснить, что возвращает redi::zip(), — посмотреть, что возвращает redi::zip(). =) Моя IDE позволяет мне перейти прямо к ней, удерживая Ctrl и нажимая zip(). Разве ваш не предлагает аналогичные функции? Я могу даже просто навести курсор на zip() в цикле for() и получить всплывающую подсказку, которая дает сигнатуру функции, включая тип возвращаемого значения.

В любом случае вам нужно будет посмотреть на него, чтобы напечатать то, что вы вручную заменили бы «auto», и auto дает большое преимущество, позволяя вам объявлять типы, которые невозможно объявить иначе (например, лямбда-возвраты, если только не делать сложные вещи, такие как decltype, который имеет тот же недостаток, который вам не нравится в auto).

Когда IDE будут больше поддерживать C++11, ваш intellisense сработает лучше, и будет понятнее, что это за тип. Я уверен, что через год или меньше большинство современных IDE сообщат вам истинный тип auto при наведении курсора.

Преимущества auto намного перевешивают потери, хотя да, есть небольшая потеря, которая станет еще меньше при хорошей поддержке IDE. Почти у всего есть плюсы и минусы.

person Jamin Grey    schedule 23.07.2013

Я не согласен с вашим утверждением, что незнание типа i «усложняет повторное использование опыта и обучение на примерах». Тип i - это "то, что возвращает zip". Почему этого недостаточно?

person Sebastian Redl    schedule 23.07.2013
comment
См. мой комментарий к Кейси выше - person user1676605; 23.07.2013
comment
Было бы здорово, если бы я мог сказать typedef struct EventData { what redi::zip::returns zip; }Данные События; Поэтому я предлагаю новое ключевое слово С++ 11, независимо от того, что оно похоже на auto, но определяет тип возвращаемого значения для меня во ВРЕМЯ КОМПИЛЯЦИИ без необходимости его назначения. - person user1676605; 23.07.2013
comment
@ user1676605: В С++ 11 вы можете сделать именно это. Только с другим синтаксисом: decltype(redi::zip(vi,vs)) zip; - person celtschk; 24.07.2013

Помимо других ответов, мне нравится использовать <boost/type_index.hpp>:

int main()
{
    using namespace std;
    using namespace boost::typeindex;

    auto p = std::make_pair(1, 2);
    cout << type_id_with_cvr<decltype(p)>().pretty_name();
}

Что выводит:

std::pair<int, int>

Вы также можете использовать typeid() из <typeinfo>:

auto p = std::make_pair(1, 2);
cout << typeid(p).name() << '\n';

Вывод не такой понятный, как в первом примере, но все же:

St4pairIiiE
person Andreas DM    schedule 10.04.2016

Ответ для

Как определить фактический тип переменной auto во время компиляции

Отвечать:

Попробуйте скомпилировать что-то вроде этого:

auto foo = function_that_returns_unknown_type() // "what type could foo be?"
int a = foo;

Сообщение об ошибке компилятора сообщит вам, что тип XXX (что бы это ни было) не может быть преобразован в int. Там у вас есть ваш тип

person DarkTrick    schedule 15.11.2020

Windows: встроенный отладчик в Visual Studio предоставит вам информацию о типе.

Linux: отладьте код в gdb и отправьте ptype <varname>, когда exe прерывается с переменной в области видимости. Пример вывода - в этом случае я мог бы заменить auto на vector<uint8_t>::const_iterator, но это не особенно очевидно из этого:

type = class __gnu_cxx::__normal_iterator
                  <unsigned char const*,
                   std::vector<unsigned char,
                               std::allocator<unsigned char>
                              > 
                  >
       [with _Iterator = const unsigned char *, 
       _Container = std::vector<unsigned char, std::allocator<unsigned char> >] 
        {
   protected:
       _Iterator _M_current;

   public:
     __normal_iterator(void);
     __normal_iterator(const unsigned char * const&);
     reference operator*(void) const;
     _Iterator operator->(void) const;
     __gnu_cxx::__normal_iterator<unsigned char const*, _Container> & operator++(void);
     __gnu_cxx::__normal_iterator<unsigned char const*, _Container> operator++(int);
     __gnu_cxx::__normal_iterator<unsigned char const*, _Container> & operator--(void);
     __gnu_cxx::__normal_iterator<unsigned char const*, _Container> operator--(int);
     reference operator[](difference_type) const;
     __gnu_cxx::__normal_iterator<unsigned char const*, _Container> & operator+=(difference_type);
     __gnu_cxx::__normal_iterator<unsigned char const*, _Container> operator+(difference_type) const;
     __gnu_cxx::__normal_iterator<unsigned char const*, _Container> & operator-=(difference_type);
    __gnu_cxx::__normal_iterator<unsigned char const*, _Container> operator-(difference_type) const;

     const unsigned char * const& base(void) const;
     void __normal_iterator<unsigned char*>(const
                        __gnu_cxx::__normal_iterator<unsigned char*, _Container> &);

     typedef std::iterator_traits<unsigned char const*>::reference reference;
     typedef _Iterator pointer;
     typedef std::iterator_traits<unsigned char const*>::difference_type difference_type;
     typedef std::iterator_traits<unsigned char const*>::iterator_category iterator_category;
 }
person Den-Jason    schedule 06.11.2019