0

Consider the following function:

using iteratorPair = std::pair<std::set<Record>::const_iterator, std::set<Record>::const_iterator>;
using iteratorList = std::vector<iteratorPair>;

template<class FunctionPtr>
iteratorList splitByParameter(iteratorPair& range, const FunctionPtr& accesorFunction)
{
    iteratorList list;

    list.push_back(range);

    auto firstValueFunction = std::bind(accesorFunction, *range.first);
    auto value = firstValueFunction();

    for (auto iterator = range.first; iterator != range.second; ++iterator)
    {
        auto iteratorValueFunction = std::bind(accesorFunction, *iterator);
        if (iteratorValueFunction() != value)
        {
            value = iteratorValueFunction();
            list.back().second = iterator;
            list.emplace_back(iterator, range.second);
        }
    }

    return list;
}

It's purpose is to create iterator ranges for records in a std::set that have a common value... e.g. all the green eyed people, and then can be called again on those subsets for get additional ranges, e.g. all the green eyed males. You pass it a range to operate on, and a function pointer to the accessor that returns the value you want to compare.

I built this with MSVC 2019 16.4.3, and was surprised that I could pass accessorFunction pointers that had different return types, so that in one invocation of the function, value might be an enum class, and in the next invocation it could be a double. I'm using various strong types (units), that are not implicitly convertible to each other.

That would make sense to me if value was a template type, but I didn't think auto could be used in that fashion? Is this just some UB or a compiler quirk, or is it actually legal to use auto this way?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Nicolas Holthaus
  • 7,763
  • 4
  • 42
  • 97
  • 4
    How would you define `auto` such that it would _not_ be usable in this way? What would that mean, exactly? – Barry Mar 24 '20 at 16:34
  • 6
    Could also just reduce the question to `template T copy(T in) { auto val = in; return val; }`, since almost all of the code (and all of its description) isn't really relevant to your specific question. – Barry Mar 24 '20 at 16:35
  • @Barry maybe just a misunderstanding on my part, but it seemed weird that the same line of code could have two different types in the same program without being a template parameter. – Nicolas Holthaus Mar 24 '20 at 16:36
  • 2
    @NicolasHolthaus: But it *is* a template parameter. Or more specifically, it is *dependent* on a template parameter. – Nicol Bolas Mar 24 '20 at 16:38
  • 4
    For a particular instantiation of the template function `splitByParameter` an `auto` variable will always have the same type. Passing in differently typed parameters creates a new instantiation of the template function `splitByParameter`. – Richard Critten Mar 24 '20 at 16:39

1 Answers1

2

auto more or less works like template argument deduction. If you call a function template with one argument being a type which is dependent on a template parameter of your template, then the exact type of that function template will be determined during the instantiation of your template.

This is how std::bind itself works; the resulting type of such a call will depend on the type of the template arguments provided to the function.

The same goes for auto. If the expression whose type is being deduced is dependent on one of your template parameters, then the type deduced will be based on that dependency. And thus, the deduction itself will have to be deferred until instantiation time.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982