Використання відрахування типу «auto» - як дізнатися, який тип вивів компілятор?


74

Як я можу дізнатись, який тип вивів компілятор при використанні autoключового слова?

Приклад 1: Простіший

auto tickTime = 0.001;

Це було виведено як a floatабо adouble?

Приклад 2: Більш складні (і мій головний біль):

typedef std::ratio<1, 1> sec;
std::chrono::duration<double, sec > timePerTick2{0.001};
 auto nextTickTime = std::chrono::high_resolution_clock::now() + timePerTick2;

Що це за тип nextTickTime?

Проблема у мене, коли я намагаюся послати nextTickTimeдо std::cout. Я отримую таку помилку:

./main.cpp: In function ‘int main(int, char**)’:
./main.cpp:143:16: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
  std::cout << std::setprecision(12) << nextTickTime << std::endl; // time in seconds
            ^
In file included from /usr/include/c++/4.8.2/iostream:39:0,
             from ./main.cpp:10:
/usr/include/c++/4.8.2/ostream:602:5: error:   initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double, std::ratio<1l, 1000000000l> > >]’
 operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)

4
Коли сумніваюся, я обманюю. Створіть дешеву хакерську програму, яка оголошує автоматичну змінну, але не використовує її, а потім перевірте, що таке налагоджувач.
user4581301

8
Я використовую, eclipse IDEі в більшості випадків я просто навожу курсор на autoключове слово, і виводиться тип вискакує.
Галік

24
Найнадійніший хак, який працює в будь-якій IDE, - просто не використовуй auto:) Серйозно, якщо вас насправді турбує, який саме тип виведений, чому б ви використовували, autoщо може призвести до різного типу за різних обставин?
Дбайливе натискання ключів

12
Емм ... чого мені не вистачає? Це прямо там у повідомленні про помилку?
Даніель Жур

2
приблизно auto tickTime = 0.001;: безf літералу - це дубль
phuclv

Відповіді:


110

Мені подобається використовувати ідею Effective Modern C ++, яка використовує нереалізований шаблон; тип виводиться з помилкою компілятора:

 template<typename T> struct TD;

Тепер для автоматичної змінної varпісля її визначення додайте:

 TD<decltype(var)> td;

І перегляньте повідомлення про помилку для вашого компілятора, воно буде містити тип var.


1
приємно, дуже розумно!
Метью Фішер

2
Б template<typename T> void td(T t);також робота для автоматичних змінного вару?
храповий дивак

4
@ratchetfreak так, але тоді ви отримаєте помилку компонувальника, а не помилку компіляції. Помилки компіляції показані раніше.
marcinj

33

Ло-фі фокус, який не вимагає попередніх визначень помічників:

typename decltype(nextTickTime)::_

Компілятор скаржиться, що _не є членом будь-якого типу nextTickTime.


Це врятувало мій день! :)
Деволус

8

Ось typeidверсія, яка використовує boost::core::demangleдля отримання імені типу під час виконання.

#include <string>
#include <iostream>
#include <typeinfo>
#include <vector>
using namespace std::literals;

#include <boost/core/demangle.hpp>

template<typename T>
std::string type_str(){ return boost::core::demangle(typeid(T).name()); }

auto main() -> int{
    auto make_vector = [](auto head, auto ... tail) -> std::vector<decltype(head)>{
        return {head, tail...};
    };

    auto i = 1;
    auto f = 1.f;
    auto d = 1.0;
    auto s = "1.0"s;
    auto v = make_vector(1, 2, 3, 4, 5);

    std::cout
    << "typeof(i) = " << type_str<decltype(i)>() << '\n'
    << "typeof(f) = " << type_str<decltype(f)>() << '\n'
    << "typeof(d) = " << type_str<decltype(d)>() << '\n'
    << "typeof(s) = " << type_str<decltype(s)>() << '\n'
    << "typeof(v) = " << type_str<decltype(v)>() << '\n'
    << std::endl;
}

Що друкує це в моїй системі:

typeof(i) = int
typeof(f) = float
typeof(d) = double
typeof(s) = std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
typeof(v) = std::vector<int, std::allocator<int> >

Чи можете ви навести приклад результату? Мені цікаво, якщо це те саме, що і в повідомленнях про помилки компілятора, Boost робить щось більш розумне?
Дуглас Геріот

5

typeid можна використовувати для отримання типу змінної більшу частину часу. Це залежить від компілятора, і я бачив, що це дає дивні результати. g ++ має RTTI за замовчуванням, не впевнений на стороні Windows.

#include <iostream>
#include <typeinfo>
#include <stdint.h>
#include <chrono>
#include <ctime>

typedef std::ratio<1, 1> sec;
int main()
{
    auto tickTime = .001;
    std::chrono::duration<double, sec > timePerTick2{0.001};
    auto nextTickTime = std::chrono::high_resolution_clock::now() + timePerTick2;
    std::cout << typeid(tickTime).name() << std::endl;
    std::cout << typeid(nextTickTime).name() << std::endl;

    return 0;
}

./a.out | c++filt

double
std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l> > >

Посилання на "сторону Windows" не має особливого сенсу. GCC працює в Windows, як і інші популярні компілятори, тому я припускаю, що ви маєте на увазі компілятор Microsoft. Дійсно, RTTI ввімкнено за замовчуванням і для MSVC. Ви повинні явно вимкнути це за допомогою /GR-. Ви отримаєте bad_typeidвиняток, якщо спробуєте використовувати typeidпід час компіляції /GR-, тому проблема буде цілком очевидною. (Звичайно, у будь-якому компіляторі C ++ за замовчуванням повинен бути включений RTTI, оскільки в іншому випадку це суто порушує мовний стандарт.)
Коді Грей

@CodyGray: Досить типово, що налаштування компілятора за замовчуванням не відповідають стандарту. Якщо вам потрібен стандартний сумісний компілятор, зазвичай вам потрібно додати деякі параметри.
Мартін Боннер підтримує Моніку

Мій досвід показує, що порівняно загальноприйняті варіанти компілятора зменшують відповідність стандартам, але вимкнення основних функцій мови, таких як RTTI або винятки, здається занадто далеко. Я не бачив звичайного (тобто невбудованого чи іншого спеціального випадку) компілятора, який би відключив будь-яку з цих речей нестандартно. @martin
Коді Грей

Цей код не використовує RTTI. Типи часу виконання цих виразів статично відомі під час компіляції, вони не є поліморфними. Він буде компілюватися і працювати бездоганно з відключеним RTTI.
Окталіст

Я отримую помилку компіляції з вимкненим RTTI під OS X. g ++ -std = c ++ 11 -g -fno-rtti junk.cpp junk.cpp: 16: 18: помилка: не вдається використовувати typeid з -fno-rtti std :: cout << typeid (tickTime) .name () << std :: endl;
Метью Фішер

3

Як сказав Даніель Жур , прочитайте повідомлення про помилку:

... _Tp = std::chrono::time_point<
           std::chrono::_V2::system_clock,
           std::chrono::duration<
             double, std::ratio<1l, 1000000000l> > > ...

1
Правда. У разі виникнення помилки компілятора тип можна знайти в повідомленні про помилку; однак питання спрямоване на випадки, коли код успішно компілюється, і немає повідомлення про помилку.
kmiklas

2

Низькотехнологічне рішення - навести мишу, на nextTickTimeяку в деяких графічних інтерфейсах видається тип else, встановлений .після nextTickTimeвcout і виберіть розумне шукають значення або функцію.

Загалом, якщо Ви знаєте, який тип Ви отримуєте, autoякщо не знаєте , не використовуйте його. Що трохи протилежне інтуїції.

Отже, якщо ви знаєте його інтератор, просто використовуйте auto, щоб зменшити заклинання, якщо результат якогось невідомого типу, вам слід з’ясувати, що це таке, перед використанням auto.

Див. Також Херб, Андрій та Скотт, які обговорюютьauto


2
"Якщо ви не знаєте, не використовуйте", немає сенсу, коли задіяні шаблони.
Бен Войгт

2

Ця відповідь SO дає приємну функцію для роздрукування імені типу (насправді кілька реалізацій).

Крім цього безкоштовно, з відкритим вихідним кодом, заголовок тільки бібліотека дає хороший спосіб роздрукувати значення і введіть в chrono::durationс.

Поєднання цих двох утиліт:

#include "chrono_io.h"
#include "type_name.h"
#include <iomanip>
#include <iostream>

int
main()
{
    using namespace date;
    typedef std::ratio<1, 1> sec;
    std::chrono::duration<double, sec > timePerTick2{0.001};
    auto nextTickTime = std::chrono::high_resolution_clock::now() + timePerTick2;
    std::cout << type_name<decltype(nextTickTime)>() << '\n';
    std::cout << std::setprecision(12) << nextTickTime.time_since_epoch() << '\n';
}

Цей результат для мене:

std::__1::chrono::time_point<std::__1::chrono::steady_clock, std::__1::chrono::duration<double, std::__1::ratio<1, 1000000000> > >
4.8530542088e+14ns

2

Тип, виведений компілятором, міститься в повідомленні про помилку:

/usr/include/c++/4.8.2/ostream:602:5: error:   initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>;
 _Tp = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<double, std::ratio<1l, 1000000000l> > >]’
  ^^   <-------- the long type name --------------------------------------------------------------------------------------->

Це складне ім’я типу, але воно є в повідомленні про помилку.


1

Як додаткове зауваження, щоб ефективно роздрукувати значення у nextTickTimeвас слід явно перетворити на відповідне std::chrono::durationі вивести результат duration::count.

using std::chrono::duration_cast;
using std::chrono::seconds;

auto baseTime = ...;
std::cout << std::setprecision(12) << duration_cast<seconds>(nextTickTime - baseTime).count()
    << std::endl; // time in seconds

1

Ось спосіб примусити помилку компіляції, яка показує тип tickTime:

struct {} baD = tickTime;

0

@ jonathan-oconnor зазначає, що ви можете використовувати [[deprecated]]атрибут, введений в C ++ 14, для створення дуже чистого рішення:

template<typename T>
[[deprecated]] constexpr void printType(T const&) {}

На жаль, у діагностиці, яку видає MSVC, тип не згадується. ( приклад )

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.