10

I'm trying to make a function which finds the minimum element in a range which satisfies a given condition:

#include <functional>
#include <iostream>
#include <vector>

template <typename It, typename Pred, typename Comp>
It minElementWhere(
    It begin,
    It end,
    Pred pred = Pred(),

    // Use less-than as the default comparator.
    Comp comp = std::less<decltype(*std::declval<It>())>()
) {
    It minElement = end;

    for (It it = begin; it != end; ++it) {
        if (!pred(*it)) {
            continue;
        }

        if (comp(*it, *minElement)) {
            minElement = it;
        }
    }

    return minElement;
}

int main() {
    std::vector<double> foo;
    foo.push_back(6);
    foo.push_back(10);
    foo.push_back(-3);
    foo.push_back(7);

    std::cout << *minElementWhere(
        foo.begin(),
        foo.end(),
        [](double val) {
            return val >= 0;
        }
    ) << std::endl;
}

But I get this error:

main.cpp: In function 'int main()':
main.cpp:40:5: error: no matching function for call to 'minElementWhere(std::vector<double>::iterator, std::vector<double>::iterator, main()::__lambda0)'
     ) << std::endl;
     ^
main.cpp:40:5: note: candidate is:
main.cpp:6:4: note: template<class It, class Pred, class Comp> It minElementWhere(It, It, Pred, Comp)
 It minElementWhere(
    ^
main.cpp:6:4: note:   template argument deduction/substitution failed:
main.cpp:40:5: note:   couldn't deduce template parameter 'Comp'
     ) << std::endl;

Comp isn't the return type, so it's not trying to deduce the return type, and it doesn't seem to me like there are ambiguous overloads of Comp (since there can only be one return type of dereferencing an It). Why am I getting this error, and how can I fix it?

2
  • 2
    It's not possible to deduce template parameters from defaulted arguments. Commented Jun 18, 2014 at 6:02
  • 1
    [OT]: your implementation is UB as you do comp(*it, *end). Commented Jun 18, 2014 at 7:11

3 Answers 3

14

You're expecting the template parameter Comp to be deduced from the default argument you provided for the corresponding function parameter. However, this is explicitly listed as a non-deduced context, meaning template argument deduction will fail for that template parameter (unless it can be deduced from elsewhere).

From §14.8.2.5/5 [temp.deduct.type]

The non-deduced contexts are:
— ...
— A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.

To have template argument deduction succeed, provide a default argument for the template parameter, instead of the function parameter.

template <typename It, 
          typename Pred, 
          typename Comp = std::less<decltype(*std::declval<It>())>>
It minElementWhere(
    It begin,
    It end,
    Pred pred = Pred(),
    Comp comp = Comp()
) {
...
}

Live demo

Sign up to request clarification or add additional context in comments.

Comments

3

You may want the following:

#include <iterator>
struct always_true{
    template<typename T>
    bool operator()(T&& val) const{
        return true;
    }
};

template <
    typename It, 
    typename Pred = always_true,
    typename Comp = std::less<typename std::iterator_traits<It>::value_type >
>
It minElementWhere(
    It begin,
    It end,
    Pred pred = Pred(),
    Comp comp = Comp()
    ){
    It minElement = end;

    for (It it = begin; it != end; ++it) {
        if (pred(*it) && (minElement == end || comp(*it, *minElement))){
            minElement = it;
        }
    }

    return minElement;
}

Comments

1

This works for me:

template <typename It, typename Pred,
          typename Comp = std::less<decltype(*std::declval<It>())>>
It minElementWhere(
    It begin,
    It end,
    Pred pred = Pred(),
    Comp comp = Comp()
) {
    It minElement = end;

    for (It it = begin; it != end; ++it) {
        if (!pred(*it)) {
            continue;
        }

        if (comp(*it, *minElement)) {
            minElement = it;
        }
    }

    return minElement;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.