简体   繁体   中英

How to deduce iterator template type, or its template's nested type?

This questions begs a more preparation, so I provide some bits of code first and then the exact question

Assuming I have the following type declared

template<typename T>
struct some_type
{
    T t_;
};

which would be constructed with a factory function like so

typedef float numeric_type;
std::vector<std::string> construction_material;
//Push_back of strings in certain form...
std::vector<std::unique_ptr<some_type<numeric_type>> instances;
build_instances(construction_material.begin(), construction_material.end(), back_inserter(instances));

and the construction function would be something like following

template<typename input_iterator, typename output_iterator>
output_iterator build_instances(input_iterator begin, input_iterator end, output_iterator out)
{
    for(input_iterator iter = begin; iter != end; ++iter)
    {
        //This won't work, but to illustrate some ideas...
        //build_instance<std::iterator_traits<output_iterator>::value_type>(*iter)
    }

    //[...]

    return *out;
}

template<typename T>
std::unique_ptr<some_type<T>> build_instance(std::string const& material)
{
    static_assert(std::is_floating_point<T>::value == true, "The template type needs to be a floating point type.");

    std::unique_ptr<some_instance<T>> instance(new some_instance<T>());
    //Some processing...

    return instance;
}

I know I could change the function to return some container (or perhaps even templatize the container type), like

template<typename input_iterator, typename T>
std::vector<std::unique_type<T>> build_instances(input_iterator begin, input_iterator end,       
output_iterator out)
{
    //Likewise code to the previous snippets...
    return ...
}

The problems I haven't been able to solve are:

  1. Would it be possible -- or impossible -- to use the back_inserter like approach? It looks like being the most flexible for the caller?
  2. How to get a hold on the numeric_type in build_instances body (as in having it through output_iterator) so that it can be used in building the instances one-by-one?
  3. How to ensure the caller knows to wait for the objects wrapped in std::unique_ptrs? An alternative would be just as plain pointers, but I'm not enthuasiastic about that.

There's a similar kind of question with a heading How can I make this template method more elegant? (or: less explicit template parameters required) , which takes a container and transforms it to a different type of a container.

Edit As commented to Jogojapan's comment that currently I transform the input like so

std::transform(construction_material.begin(), construction_material.end(), std::inserter(instances, instances.begin()), build_instance<numeric_type>);

but the subsequent function calls need to be supplied the numeric_type typedef too, which is somewhat cumbersome. I hope to avoid that. It looks I was mistaken, but for the purpose of education and all, would it be possible to further reduce the need to typedef the numeric type and deduce it from the iterator?

An intrusive solution is to let some_type exposes its type parameter, the same way std::unique_ptr<T, D> exposes its first parameter via element_type (which we will need later):

template<typename T>
struct some_type
{
    // give it an appropriate meaningful name
    using value_type = T;
    value_type t_;
};

template<typename input_iterator, typename output_iterator>
output_iterator build_instances(input_iterator begin, input_iterator end, output_iterator out)
{
    using pointer_type = typename std::iterator_traits<output_iterator>::value_type;
    using value_type = typename pointer_type::element_type::value_type;
    return std::transform(begin, end, out, build_instance<value_type>);
}

You can also non-intrusively extract the first template parameter of a template specialization:

template<typename T>
struct first;

template<template<typename...> class Template, typename First, typename... Pack>
struct first<Template<First, Pack...>>> {
    using type = First;
};

template<typename T>
using First = typename first<T>::type;

The value_type alias in build_instances would instead become

using value_type = First<typename pointer_type::element_type>;

As a final remark I find it a bit odd that build_instance take a T parameter but constructs instances of some_type<T> . If it took T and constructed instances of T (where perhaps T is restricted to be a specialization of some_type .) This would have spared your problem, too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM