Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value_type of a container template parameter

Tags:

c++

std

In his keynote of this years Going Native The Essence of C++ (go to 40:30) Bjarne Stroustrup gives the following code example:

template<typename C, typename V>
vector<Value_type<C>*> find_all(C& cont, V v) 
{
    vector<Value_type<C>*> res; 

    for (auto& x : cont) 
        if (x == v) 
            res.push_back(&x)

    return res;
}

This function is used to find all occurrences of a value in a container and returns pointers to the found elements. The example from the video:

string m{"Mary had a little lamb"}; 
for (const auto p: find_all(m,'a')) // p is a char*
    if (*p != 'a')
        cerr << "string bug!\n"; 

My question is about Value_Type<C>*. Is there something like this in the standard library? I looked for it and didn't find it. How could this be implemented, if it's not in std?

like image 201
guini Avatar asked Nov 12 '13 14:11

guini


2 Answers

I don't know of this in the standard, but it's not hard to implement:

    template <class C>
    struct value_type
    {
       typedef typename C::value_type type;
    };

    template <class T, int N>
    struct value_type<T[N]>
    {
       typedef T type;
    };

    template <class T>
    struct value_type<T*>
    {
      typedef T type;
    };

and now you can use typename value_type<C>::type to access the type that a container contains. If you have your own container you would like to use but it doesn't have a value_type typedef (and for whatever reason you can't change it) then you can simply specialize this struct for that container as well.

To avoid the typename ...::type you can do:

    template <class C>
    using Value_Type = typedef value_type<C>::type;

and now you just use Value_Type<C> everywhere.

EDIT
As stefan suggested in soon's answer, you can do this more easily with std::begin which is okay because any container you use/create you would want to be able to call std::begin and std::end on anyway:

    template <class C>
    using Value_Type = typename std::remove_reference<
        decltype(*std::begin(std::declval<
            typename std::add_lvalue_reference<C>::type>()))>::type;

This is much more concise, though it's gotten a little dense to read. It's still better than the first option, this will require less boilerplate code for custom container types.

like image 130
SirGuy Avatar answered Oct 22 '22 23:10

SirGuy


Value_type<C> is just a typedef for C::value_type. As far, as I know, there is no such typedef in standard library, but you could define it yourself:

template <class T>
using Value_type = typename T::value_type;

template<typename C, typename V>
std::vector<Value_type<C>*> find_all(C& cont, V v)
{
    std::vector<Value_type<C>*> res;

    for (auto& x : cont)
        if (x == v)
            res.push_back(&x);

    return res;
}

int main()
{
    std::vector<int> v{1, 2, 3, 3, 5};

    for(const auto x: find_all(v, 3))
    {
        std::cout << *x << std::endl;
    }
}

But, as suggested by @stefan, this will work with standard containers only. You could retrieve underlying type with using std::begin function(which is defined for arrays too), how it's implemented in @GuyGreer's answer

like image 3
awesoon Avatar answered Oct 22 '22 23:10

awesoon