22

In this response:

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

this program is given:

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>() << ' ';

I have no idea what the type of auto i is, making it harder to reuse expertise and learn from examples. Here is what changing auto i into char i returns

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) ===|

Try to figure out the type from that.

Is there a way to figure out what the type a variable of an auto is in C++11? To be more clear, I have a struct like this:

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;
};
Community
  • 1
  • 1
user1676605
  • 1,337
  • 3
  • 13
  • 21
  • 1
    Check the reference for what `redi::zip` returns. – Felix Glas Jul 23 '13 at 18:23
  • 3
    (A) use an IDE, (B) don't use `auto` if you don't know enough about the type to use it – Mooing Duck Jul 23 '13 at 18:23
  • 4
    If you don't mind getting an answer at runtime you could always do someting like `std::cout << typeid(i).name() << std::endl;` – Borgleader Jul 23 '13 at 18:27
  • you're right, but at least i can spot that it's some kind of iterator – nio Jul 23 '13 at 18:34
  • @nio The range based for loop actually uses the iterators provided by either the free or the member function `begin()` or array bounds but the type of `i` will be whatever the dereferencing operator returns for that iterator. – Pixelchemist Jul 23 '13 at 18:59
  • 5
    It doesn't matter what the type is, only how you can use it. – Cat Plus Plus Jul 23 '13 at 21:18
  • @nio, it's not an iterator, it's an iterator's `reference` type, the result of dereferencing an iterator – Jonathan Wakely Jul 23 '13 at 21:43
  • Do you want the return type of `redi::zip(vi, vs)` or do you want the type of `i` in the loop? They're not the same. Please clarify the question. – Jonathan Wakely Jul 23 '13 at 21:45
  • If you're still writing `typedef struct EventData { } EventData`, you _really_ should pick up a C++ book. It's still allowed for compatibility with C, but your code obviously relies on C++. – MSalters Jul 29 '13 at 15:25
  • "Is there a way to figure out what the type a variable of an auto is?", yes [here!](https://stackoverflow.com/questions/38820579/using-auto-type-deduction-how-to-find-out-what-type-the-compiler-deduced/38839265). – PLG Sep 20 '18 at 19:39

9 Answers9

28

Try to change auto into a char and read the error message.

nio
  • 5,141
  • 2
  • 24
  • 35
10

Why do you want to put that type in a struct? It's not really designed to be used like that (I should know, I wrote it!) but if necessary you can use decltype and std::declvalto determine the type (which will still give the right answer if I change the implementation of 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;
};

N.B. why are you creating a typedef for the struct? This is C++ not C, stop it.

I have no idea what the type of auto i is, making it harder to reuse expertise and learn from examples.

Get used to it. Do you know the type that std::bind returns? Do you know the type that std::mem_fn returns? Do you know the type that a lambda expression creates? No, you don't need to know, all you need to know is what properties it has and what you can do with it, not what it's called or what types it contains.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • As I mentioned in another comment, I'm not sure if this is of any help, since `redi::zip` needs the original ranges to stay alive. – Xeo Jul 23 '13 at 21:32
  • I don't think he actually wants the range, he seems to want the the type from dereferencing an iterator into the zip range. – Mooing Duck Jul 23 '13 at 21:32
  • Also, about `std::bind` and your other examples, we have `std::function` for storing those for exactly this reason. – Mooing Duck Jul 23 '13 at 21:35
  • @MooingDuck, hmm, you may be right about the type (although he says he wants _"return value from redi::zip"_ which is obviously different from that type's `value_type`) -- answer updated to give that type. – Jonathan Wakely Jul 23 '13 at 21:39
  • @MooingDuck, yes exactly, `std::function` works with suitable types where you know _what you can do with it_ not exactly what type it is. – Jonathan Wakely Jul 23 '13 at 21:39
  • @Jonathan: Ah, I underestimated it. Nvm then. :) – Xeo Jul 23 '13 at 21:42
  • @Xeo, I am adamant that any range-like types discussed by the ISO committee **must not** leave dangling refs when used with rvalues ... and `redi::zip` is a proof of concept for how to do it right :) – Jonathan Wakely Jul 23 '13 at 21:48
  • The reason is that I want to throw an event to another thread, and in that thread I want to do the iteration to extract the zipped values. The struct is the data being thrown, and it packages a redi::zip. – user1676605 Jul 23 '13 at 21:52
  • @user1676605, so either use `decltype` or you could store the containers themselves and do the zipping in the other thread. – Jonathan Wakely Jul 23 '13 at 21:56
  • Can someone show how to use std::function or decltype inside a struct to extract the values? – user1676605 Jul 23 '13 at 21:57
  • @user1676605, you can't use `std::function` for this - you can use `std::function` to wrap callable types (such as the ones returned from `std::bind` and `std::mem_fn`) not range-like types as returned by `redi::zip`. – Jonathan Wakely Jul 23 '13 at 21:58
  • If this suggestion works it is PERFECT: "@user1676605: In C++11 you can do exactly that. Just with a different syntax: decltype(redi::zip(vi,vs)) zip; – celtschk 38 mins ago" – user1676605 Jul 23 '13 at 22:02
  • @JonathanWakely It seems like "operator auto" could solve this issue with "dangling" ranges as well as the expression template problem. – Casey Jul 23 '13 at 22:04
  • @jonathan, when I use your struct, I get this error: using iterator = decltype(std::begin(std::declval())); ../REDI/zip.h|38|error: no matching function for call to ‘begin(PriceQuote&)’| PriceQuote is V1, V2 in your struct above – user1676605 Jul 24 '13 at 12:42
  • Does `PriceQuote` have a `begin()` member function? – Jonathan Wakely Jul 24 '13 at 14:46
  • @jonathan, no it does not. Why would it, it is just a data struct? See original message for what PriceQuote looks like. – user1676605 Jul 31 '13 at 22:17
  • I don't know why it would or wouldn't, you're the one trying to zip a `PriceQuote`, that's what the error means. I expect you want to zip a `vector` but the error suggests that's not what you did – Jonathan Wakely Jul 31 '13 at 23:58
9

Would you have found

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>() << ' ';

easier to understand?

Casey
  • 41,449
  • 7
  • 95
  • 125
  • Good point, but then, how do I assign the return value of redi::zip to my own variable without making it auto? typedef struct EventData { /*what do I put here? */ } – user1676605 Jul 23 '13 at 18:40
  • 1
    As others have said above, it's not important what type it is, so much as what you can do with it. Just from the example code, I know that "what I can do with it" is iterate over some kind of range of `boost::tuple`. I would use a container of said tuples for storage `std::vector>` and fill it with `boost::range::copy(redi::zip(vi, vs), myvector);`. – Casey Jul 23 '13 at 18:50
  • 1
    Great, so now you are adding copy to a program that did not require it so that you can store it? See my modified original question. – user1676605 Jul 23 '13 at 18:52
  • The answer to the question you cited explicitly states: "See for a zip function which works with range-base[d] for..." so if that's what you really want: `for(auto i: redi::zip(vi, vs)) myvector.push_back(i);` will do it. I prefer the algorithm to the explicit copy. – Casey Jul 23 '13 at 19:07
  • If you simply want to directly initialize a container, you should learn how to use the `zip_iterator` that `redi::zip` is wrapping up in a range for you: http://www.boost.org/doc/libs/1_54_0/libs/iterator/doc/zip_iterator.html. – Casey Jul 23 '13 at 19:08
  • I am not being clear. I want to have a variable inside a struct of whatever the return type of redi::zip is withtout any performance penalty and without having to figure out ten levels deep template gibberish. Like this typedef struct EventData{//return value from redi::zip what goes here????? } EventData; – user1676605 Jul 23 '13 at 19:14
  • As I said below, It would be great if I could say typedef struct EventData { whatever redi::zip::returns zip; } EventData; So I suggest a new c++11 keyword, "whatever" or "whateverreturn". It is similar to auto, but figures out the return type for me at COMPILE TIME without it needing being assigned to. I can't say "auto i" inside the struct right without assignment? – user1676605 Jul 23 '13 at 19:17
  • 4
    @user1676605: `decltype` would like a word with you. However, that likely won't help you - `redi::zip` returns a lazy range that *depends* on the original ranges being alive. It just uses iterators into those original ranges, so if they are gone, the returned range from `redi::zip` is dangling. – Xeo Jul 23 '13 at 21:15
  • @Xeo: The original ranges may be alive still, we don't know that they aren't (Unless the construction of `redi::zip` creates temporary ranges from the vectors?) – Mooing Duck Jul 23 '13 at 21:23
  • `redi::zip` takes ownership of rvalue ranges (by moving them into its own member variables) and holds references to lvalue ranges. It's not meant to be stored, it's meant to be created and used immediately (e.g. in a range-based `for` loop) so Xeo's point is a good one in general. – Jonathan Wakely Jul 23 '13 at 21:54
  • this seems to do it: "@user1676605: In C++11 you can do exactly that. Just with a different syntax: decltype(redi::zip(vi,vs)) zip; – celtschk 38 mins ago" – user1676605 Jul 23 '13 at 22:02
8

An answer for

"How can I determine the actual type of an 'auto' variable at compile time"

Answer:

Try compiling something like this:

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

The compiler error message will tell you that "type XXX (whatever that is) cannot be converted to int". There you have your type

DarkTrick
  • 2,447
  • 1
  • 21
  • 39
3

The best way to figure out what redi::zip() returns is to look at what redi::zip() returns. =) My IDE lets me jump straight to it by holding Ctrl and clicking zip(). Doesn't yours offer similar features? I can even just hover over zip() in the for() loop, and get a tooltip that gives the function signature - including the return type.

You'll need to look at it anyway for typing whatever you'd manually replace 'auto' with, and auto serves the great benefit that it lets you declare types that it's impossible to otherwise declare (like lambda returns, unless doing complex things like decltype, which has the same flaw you don't like about auto).

When IDEs support C++11 more, your intellisense would kick in better, and it'll be clearer what the type is. I'm sure in a year or less, most up-to-date IDEs will tell you the auto's true type when hovered over.

The gains of auto far outweigh the losses, though, yes, there is a tiny loss that will become even tinier with good IDE support. Almost everything has pros and cons.

Jamin Grey
  • 10,151
  • 6
  • 39
  • 52
2

I disagree with your assertion that not knowing the type of i is "making it harder to reuse expertise and learn from examples". The type of i is "that thing that zip returns". Why isn't that sufficient?

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • See my comment to Casey above – user1676605 Jul 23 '13 at 18:42
  • It would be great if I could say typedef struct EventData { whatever redi::zip::returns zip; } EventData; So I suggest a new c++11 keyword, "whatever" It is similar to auto, but figures out the return type for me at COMPILE TIME without it needing being assigned to. – user1676605 Jul 23 '13 at 19:05
  • 2
    @user1676605: In C++11 you can do exactly that. Just with a different syntax: `decltype(redi::zip(vi,vs)) zip;` – celtschk Jul 23 '13 at 21:22
2

Apart from other answers, I like to use <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();
}

Which outputs:

std::pair<int, int>

You can also use typeid() from <typeinfo>:

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

The output is not as understandable as the first example, but still:

St4pairIiiE
Andreas DM
  • 10,685
  • 6
  • 35
  • 62
1

Windows: The built-in debugger on Visual Studio will give you the type info.

Linux: debug the code in gdb and submit ptype <varname> when the exe breaks with the variable in scope. Example output - in this case I could replace auto with vector<uint8_t>::const_iterator but that isn't particularly obvious from this:

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;
 }
Den-Jason
  • 2,395
  • 1
  • 22
  • 17
0

You can include the <typeinfo> header, which has a function called typeid(variable_you_want_to_know).name(). This will give you the type of your variable:

#include <iostream>
#include <typeinfo>

using namespace std;

int main(){

    int a = 5;

    cout<<"Type of 'a' is : "<<typeid(a).name()<<"\n";
    
    auto k = 547.745;

    cout<<"Type of 'k' is : "<<typeid(k).name()<<"\n";

    return 0;
}

Output:

Type of 'a' is : i     // Int

Type of 'k' is : d     // Double
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83