81

How can I find out what type the compiler deduced when using the auto keyword?

Example 1: Simpler

auto tickTime = 0.001;

Was this deduced as a float or a double?

Example 2: More complex (and my present headache):

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

What type is nextTickTime?

The problem I'm having is when I try to send nextTickTime to std::cout. I get the following error:

./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)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kmiklas
  • 13,085
  • 22
  • 67
  • 103
  • 4
    When in doubt, I cheat. Make a cheap hack program that declares the auto variable but doesn't use it, then check what the debugger thinks it is. – user4581301 Aug 08 '16 at 02:48
  • Which debugger are you using? – kmiklas Aug 08 '16 at 02:48
  • Trick works in Visual Studio. Can't remember if it works in gdb. – user4581301 Aug 08 '16 at 02:49
  • 8
    I use `eclipse IDE` and most of the time I just hover the mouse over the `auto` keyword and the deduced type pops up. – Galik Aug 08 '16 at 02:53
  • 1
    @Galik speaks the truth. Just fired up Eclipse to test if it worked in GDB and didn't have to go that far. It does work, though. Small problem: Can't seem to convince `timepoint` to sum with a `double`. – user4581301 Aug 08 '16 at 02:58
  • Oops sorry, I that was supposed to be the duration. Will fix. – kmiklas Aug 08 '16 at 03:04
  • 24
    The most reliable hack which works in any IDE - Just Dont Use `auto` :) Seriously, if you're really concerned about which type deduced exactly why would you use `auto` which can result in different type under diferent circumstances? – Diligent Key Presser Aug 08 '16 at 03:05
  • There's defined rules that compilers adhere to, to determine what type it will be. If you're worried about what type it will be deduced as, explicitly type it as `Diligent` suggested. – Brandon Aug 08 '16 at 03:11
  • 1
    Totally understand the "don't use `auto`" argument, but there are times when you are trying to stuff a result into what you think the type should be, and the error messages take quite a bit of parsing to figure out what the type really is. A hack with `auto` can show you the way really fast. – user4581301 Aug 08 '16 at 03:29
  • 1
    [Howard's response (and improvement)](http://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/20170989#20170989) was interesting; `type_name()` prints `std::chrono::time_point > >` for your case... although I may actually like @mrtnj's answer better :). – mkal Aug 08 '16 at 03:31
  • 12
    Ehm ... what am I missing? It's right there in the error message? – Daniel Jour Aug 08 '16 at 06:19
  • 2
    about `auto tickTime = 0.001;`: [without `f`](http://stackoverflow.com/q/3696902/995714) the literal is a [double](http://stackoverflow.com/q/12205141/995714) – phuclv Aug 08 '16 at 09:10
  • 2
    @DiligentKeyPresser blasphemy! Ch2, item5 of Meyers' Effective Modern C++ clearly states, "Prefer ``auto`` to explicit type declarations." – kmiklas Nov 10 '17 at 14:15

11 Answers11

117

I like to use idea from Effective Modern C++ which uses non-implemented template; the type is output with compiler error:

 template<typename T> struct TD;

Now for auto variable var, after its definition add:

 TD<decltype(var)> td;

And watch error message for your compiler, it will contain type of var.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
marcinj
  • 48,511
  • 9
  • 79
  • 100
38

A lo-fi trick that doesn't require any prior helper definitions is:

typename decltype(nextTickTime)::_

The compiler will complain that _ isn't a member of whatever type nextTickTime is.

John McFarlane
  • 5,528
  • 4
  • 34
  • 38
  • Does not work. However just `decltype(example)::_;` does work. I've submitted an edit to this answer but while that will probably stay pending ~forever I thought I'd include a comment as well. – Mihai Jun 01 '23 at 16:24
  • Can you provide an example of it not working. Here's [an example](https://godbolt.org/z/GPdsWWc8M) of `typename decltype(nextTickTime)::_` working. – John McFarlane Jun 02 '23 at 11:45
  • [Example with `typename` not working](https://godbolt.org/z/89z7WPrv8). If however you replace with `decltype(nextTickTime)::_;` it will will give you the correct error. – Mihai Jun 04 '23 at 00:07
  • Was about to suggest an answer and saw you already did that. +1 – John McFarlane Jul 05 '23 at 18:48
10

Here's a typeid version that uses boost::core::demangle to get the type name at runtime.

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

Which prints this on my system:

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> >
RamblingMad
  • 5,332
  • 2
  • 24
  • 48
  • Can you give example output? I’m curious if it’s the same as in the compiler error messages, of it Boost does something more clever? – DouglasHeriot Aug 11 '16 at 17:10
5

typeid can be used to get the type of variable most of the time. It is compiler dependent and I've seen it give strange results. g++ has RTTI on by default, not sure on the Windows side.

#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> > >
Matthew Fisher
  • 2,258
  • 2
  • 14
  • 23
  • Referring to "the Windows side" doesn't make much sense. GCC works on Windows, and so do other popular compilers, so I assume you're referring to Microsoft's compiler. Indeed, RTTI is on by default for MSVC as well. You have to explicitly turn it off with `/GR-`. You'll get a `bad_typeid` exception if you try to use `typeid` when compiling with `/GR-`, so the problem will be quite obvious. (Of course, *any* C++ compiler would have to have RTTI on by default because otherwise, it is in utter violation of the language standard.) – Cody Gray - on strike Aug 08 '16 at 08:47
  • @CodyGray: It's quite common for the default setting of a compiler not to be in accord with the standard. If you want a standard compliant compiler, you usually need to add some options. – Martin Bonner supports Monica Aug 08 '16 at 10:18
  • My experience suggests it is relatively common for a compiler's default options to *relax* standards-compliance, but disabling fundamental language features like RTTI or exceptions seems much too far. I haven't seen a normal (i.e., non-embedded or other special use case) compiler that disables either of these things out-of-the-box. @martin – Cody Gray - on strike Aug 08 '16 at 11:29
  • This code doesn't use RTTI. These expressions' runtime types are statically known at compile time, they are not polymorphic. It will compile and run flawlessly with RTTI disabled. – Oktalist Aug 08 '16 at 14:28
  • I get a compile failure with RTTI disabled under OS X. g++ -std=c++11 -g -fno-rtti junk.cpp junk.cpp:16:18: error: cannot use typeid with -fno-rtti std::cout << typeid(tickTime).name() << std::endl; – Matthew Fisher Aug 08 '16 at 14:33
  • g++ outputs the type's mangled name when you use `type_info::name()`, while MSVC outputs the demangled name (i.e. g++ outputs `int` as `i`, while MSVC sees it internally as `H` and outputs `int`). To get the mangled name on MSVC, you have to use the non-standard `type_info::raw_name()` (which seems to prepend a period for some reason, so it would give `.H` for `int` instead of `H`). This might explain some of the strange results. – Justin Time - Reinstate Monica Aug 08 '16 at 16:08
2

A low tech solution is hover the mouse over nextTickTime which in some GUIs gives the type else set a . after nextTickTime in the cout and select a reasonable looking value or function.

In general if You know what type You get use auto if you don't know it don't use it. Which is a bit counter intuitive.

So if you know its a interator just use auto to reduce the incantations, if the result is some unknown type you have to find out what it is before using auto.

See also Herb, Andrei and Scott discussing auto

Surt
  • 15,501
  • 3
  • 23
  • 39
2

As Daniel Jour said, read the error message:

... _Tp = std::chrono::time_point<
           std::chrono::_V2::system_clock,
           std::chrono::duration<
             double, std::ratio<1l, 1000000000l> > > ...
Community
  • 1
  • 1
Jacob Krall
  • 28,341
  • 6
  • 66
  • 76
  • 2
    True. In the case where a compiler error is thrown the type can be found in the error message; however, the question is directed at cases where the code successfully compiles, and there is no error message. – kmiklas Aug 08 '16 at 17:01
2

Here is a way to force a compile error, which shows the type of tickTime:

struct {} baD = tickTime;
pts
  • 80,836
  • 20
  • 110
  • 183
1

The type deduced by the compiler is in the error message:

/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 --------------------------------------------------------------------------------------->

It's a complicated type name but it is there in the error message.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

Add this to code:

decltype(whateverYouWantTheTypeOf)::_;

Your compiler will give you an error will reveal the type of whateverYouWantTheTypeOf (could even be an expression). Example errors:

# clang++ error on something defined as auto
type 'decltype(length)' (aka 'long') cannot be used prior to '::' because it has no members
        decltype(length)::_;
# g++ error on an expression
error: ‘_’ is not a member of ‘__gnu_cxx::__normal_iterator<float*, std::vector<float> >::difference_type’ {aka ‘long int’}
   27 |  decltype(last - first)::_;
Mihai
  • 1,261
  • 2
  • 12
  • 19
  • Based on John McFarlane's answer but fixed to work in all cases. I did already try submitting an edit. – Mihai Jun 05 '23 at 14:01
0

As a side note, to effectively print out the value in nextTickTime you should explicitly convert to a suitable std::chrono::duration and output the result of 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
obataku
  • 29,212
  • 3
  • 44
  • 57
-1

@jonathan-oconnor points out that you can use the [[deprecated]] attribute introduced in C++14 to produce a very clean solution:

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

Unfortunately though, the diagnostic emitted by MSVC doesn't mention the type. (example)

John McFarlane
  • 5,528
  • 4
  • 34
  • 38