My first (simple) template that does more than storing a value of type T
Apart from cosmetic details, I see two things that could be improved IMHO.

The template comparator R&lt;V&gt;

As a template argument, you are asking for template &lt;class&gt; class R
which is then used so R&lt;V&gt; r;

This narrows the possibilities since you can't pass a non-template comparator.
For instance, if you have a custom type MyType and a custom comparator for that type MyTypeComp then you won't be able to use it with your Extremum struct because MyTypeComp doesn't accept template arguments. 

The fix is easy:

template &lt;
          typename V,
          class R,
          typename I = void
         &gt;
struct Extremum {
    typedef V    value_type;
    typedef I    index_type;
    typedef R relation_type;
    V value;
    I index;
    R r;
    //...


And you would use it so:

Extremum&lt;int, std::greater&lt;int&gt;, int&gt; e(0,0);
Extremum&lt;MyType, MyTypeComp, int&gt; e(MyZero,0);


The index

You write almost the same code twice: once with index, once without.
You could avoid it by passing the index in a pair.
So you have only one class Extremum&lt;V, R&gt; and you set V = std::pair&lt;Value, Index&gt; and an appropriate comparator that compares two pairs according to their Value entry only.

template &lt;class V, class R&gt;
struct Extremum {
    typedef V    value_type;
    typedef R    relation_type;
    V value;
    R r;
    bool operator() (V v){
        bool result = r(v,value);
        if (result) {
            value = v; //you can use std::move here
        }
        return result;
    }
    Extremum(V v, R r) : value{v}, r{r} {} //you can use std::move here too
    std::string toString() const {
        std::stringstream s;
        s &lt;&lt; "value= " &lt;&lt; value;
        return s.str();
    }
};

template &lt;class RV&gt;
struct CompByFirstEntry {
    RV delegate;
    CompByFirstEntry(RV delegate);

    template &lt;class V, class I&gt;
    bool operator() (std::pair&lt;V,I&gt; const&amp; lhs, std::pair&lt;V,I&gt; const&amp; rhs) const {
        return delegate(lhs.first, rhs.first);
    }

    //or, even:

    template &lt;class Pair&gt;
    bool operator() (Pair const&amp; lhs, Pair const&amp; rhs) const {
        return delegate(lhs.first, rhs.first);
    }
} 




EDIT : Here is a working example.


What changed: define the constructor CompByFirstEntry::CompByFirstEntry and operator&lt;&lt; for std::pair; choose one version of the operator().
Note: you can also create a class ExtremumIndexed&lt;V, I, R&gt; that extends Extremum&lt;std::pair&lt;V,I&gt;, MyPairComp&lt;R&gt;&gt; or simply a template using directive.




std::ostream&amp; operator&lt;&lt;(std::ostream&amp; out, std::pair&lt;int, int&gt; const&amp; p)
{
    out &lt;&lt; p.first &lt;&lt; " " &lt;&lt; p.second;
    return out;
}

template &lt;class V, class R&gt;
struct Extremum {
    typedef V    value_type;
    typedef R    relation_type;
    V value;
    R r;
    bool operator() (V v){
        bool result = r(v,value);
        if (result) {
            value = v; //you can use std::move here
        }
        return result;
    }
    Extremum(V v, R r) : value{v}, r{r} {} //you can use std::move here too
    std::string toString() const {
        std::stringstream s;
        s &lt;&lt; "value = " &lt;&lt; value;
        return s.str();
    }
};

template &lt;class RV&gt;
struct CompByFirstEntry {
    RV delegate;
    CompByFirstEntry(RV delegate) : delegate{delegate} {}

    template &lt;class V, class I&gt;
    bool operator() (std::pair&lt;V,I&gt; const&amp; lhs, std::pair&lt;V,I&gt; const&amp; rhs) const {
        return delegate(lhs.first, rhs.first);
    }
};

int main(int argc, const char * argv[]) {
    using MyPairComp = CompByFirstEntry&lt;std::greater&lt;int&gt;&gt;;
    MyPairComp comp(std::greater&lt;int&gt;{});
    int value = 1;
    int index = 0;

    Extremum&lt;std::pair&lt;int,int&gt;, MyPairComp&gt; e({value, index}, comp);
    std::cout &lt;&lt; e.toString() &lt;&lt; std::endl;

    e({value + 1, index - 1});
    std::cout &lt;&lt; e.toString() &lt;&lt; std::endl;

    return 0;
}