Value_type of a container template parameter

北城余情 提交于 2019-12-10 14:38:22

问题


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?


回答1:


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.




回答2:


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



来源:https://stackoverflow.com/questions/19931199/value-type-of-a-container-template-parameter

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!