why I need to use std::decay in the following case?

1897 views c++
1

I am a newbie in C++ and made a test program to learn more about decltype, std::decay, std::is_same_v(traits) and also typeid.

I have following simple class, in which I wanted to get the template parameter type Type in the Base class's constructor using decltype.

like decltype(content of std::vector<Type>::iterator::begin). Some how, it did not work.

#include <iostream>
#include <vector>
#include <type_traits>
#include <initializer_list>
#include <typeindex>

template<typename Type> class Base
{
    std::vector<Type> vec;
public :
    Base(std::initializer_list<decltype(*vec.begin())> liVec): vec(liVec) {}
    // here                    ^^^^^^^^^^^^^^^^^^^^^^^^ . isn't enough?? 
};

int main()
{
    //Base<int> obj{ 1, 2, 3, 4, 5 };     // does not works: error !

    // to see the type, I wrote the following code
    std::vector<int> vec;
    std::cout << typeid(decltype(*vec.begin())).name() << std::endl; // prints i means int type(I know not reliable though)
    // and
    std::cout << std::boolalpha << std::is_same_v<int, decltype(*vec.begin())> << std::endl; // prints false 

    return 0;
}

The error on the line Base<int> obj{ 1, 2, 3, 4, 5 }; was(in GCC 6.1, C++14)

include\c++\initializer_list||In instantiation of 'class std::initializer_list<int&>':|
include\c++\initializer_list|54|error: forming pointer to reference type 'int&'|
include\c++\initializer_list|55|error: forming pointer to reference type 'int&'|
main.cpp|17|error: no matching function for call to 'Base<int>::Base(<brace-enclosed initializer list>)'|
candidate: 'Base<Type>::Base(std::initializer_list<decltype (*((Base<Type>*)(void)0)->Base<Type>::vec.begin())>) [with Type = int; decltype (*((Base<Type>*)(void)0)->Base<Type>::vec.begin()) = int&]'|
main.cpp|11|note:   candidate expects 1 argument, 5 provided|
main.cpp|7|note: candidate: 'Base<int>::Base(const Base<int>&)'|
main.cpp|7|note:   candidate expects 1 argument, 5 provided|
main.cpp|7|note: candidate: 'Base<int>::Base(Base<int>&&)'|
main.cpp|7|note:   candidate expects 1 argument, 5 provided|

As I saw the compiler told me something about int&, I simply tried it with following.(i.e, std::decay_t) and it worked.

template<typename Type> class Base
{
    std::vector<Type> vec;
public :
    Base(std::initializer_list<std::decay_t<decltype(*vec.begin())>> liVec): vec(liVec) {}
                               ^^^^^^^^^^^^^^^^^^^^^^^^ why I need this here?
};

int main()
{
    Base<int> obj{ 1, 2, 3, 4, 5 };     // works now

    std::vector<int> vec;
    std::cout << typeid(decltype(*vec.begin())).name() << std::endl; // prints i means int type(not reliable though)
    // and
    std::cout << std::boolalpha << std::is_same_v<int, std::decay_t<decltype(*vec.begin())>> << std::endl; // true now: WHY?

    return 0;
}

But I don't know the meaning of the error and why it worked. Could someone explain me what exactly happend?

answered question

typeid does not care about reference or const qualifiers of types, so even if you see i, it might be int, const int, int &, const int&, and so on.

@Holt Oh.. that makes sence. Then it looks like typeid is not really usful in any cases. isn't it?

typeid gives you a std::type_info, which in turns can give you a std::type_index which can be use as key in associative container, so this can be used to map "type" to whatever you want. This may have some use in specific cases.

1 Answer

10

The first line of your error messages says (among other things):

instantiation of 'class std::initializer_list<int&>'

So it is trying to create an initializer_list with a reference type which is not allowed.

Looking at your template code you have:

 std::initializer_list<decltype(*vec.begin())>

Now vec.begin() yields an iterator and dereferencing an iterator yields a reference so you can do things like:

*iter = whatever;

So you need to remove the reference part of the type. std::decay_t does that.

posted this

Have an answer?

JD

Please login first before posting an answer.