3

I would like to use my own comparator for std::set, which needs a parameter to be passed to its constructor:

template <class T = double> class EpsCompare {
  public:
    EpsCompare(T input_eps) : _eps(input_eps) {};
    bool operator() (const T & element1, const T & element2) const {
       return ( round(element1*_eps) < round(element2*_eps) );
    }
    T _eps;
};

How do I pass this to the declaration of my set data type? I would like to do something like this:

std::set<double, EpsCompare<double>(1e-5)> myEpsSet;

This does not compile, so how else can it be done?

Natan Streppel
  • 5,759
  • 6
  • 35
  • 43
urps
  • 366
  • 1
  • 4
  • 13
  • Pass an instance to the constructor: http://en.cppreference.com/w/cpp/container/set/set – juanchopanza Jul 01 '14 at 18:50
  • You **will** get burnt by this comparator. Never. Ever. Ever. Use. An. Epsilon. When. Sorting. Numbers. Ever. – n. m. could be an AI Jul 01 '14 at 18:52
  • @n.m. Why? And I don't even think this here is an epsilon in the typical sense. – Angew is no longer proud of SO Jul 01 '14 at 19:04
  • @Angew You are right, this is not a usual fuzzy comparison, my eyes see what they want, not what's written :( As for why, a fuzzy comparison is not a strict weak ordering, so the usual algorithms just won't work. Imagine a sequence of numbers, each one within epsilon of the next, but the extremes are far away; how would you sort them? – n. m. could be an AI Jul 01 '14 at 19:10
  • The second template parameter to set should be an appropriate class, here it is an instance of a class (but not a class itself) – copper.hat Jul 01 '14 at 19:20

3 Answers3

5
std::set<double, EpsCompare<> > myEpsSet(EpsCompare<>(1e-5));

or

std::set<double, EpsCompare<double> > myEpsSet(EpsCompare<double>(1e-5));
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Is there any reason to pass `EpsCompare` as second parameter to `std::set` template? – Slava Jul 01 '14 at 19:19
  • 1
    @Slava, yes that is needed. Unlike function templates, template parameters are not deduced from the arguments used to construct an instance of a class template. – R Sahu Jul 01 '14 at 19:23
  • I do not follow. Needed for what? If you do not pass it to template, only to `std::set` ctor, what difference will it make? – Slava Jul 01 '14 at 19:26
  • @Slava The second template parameter of `std::set` sets he type of the comparator. By default, it's `std::less` (where `T` is the first template parameter). But you don't want an to use object of type `std::less`, you want an object of type `EpsCompare`. – Angew is no longer proud of SO Jul 02 '14 at 06:59
  • My problem was actually a bit more complex, since I did not try to call `myEpsSet` directly, but within a class declaration. But then using the same in the initializer of the new class constructor did the job: `class Testclass { public: std::set > myEpsSet; Testclass() : myEpsSet(EpsCompare(1e-5)) {}; } ` – urps Jul 02 '14 at 11:24
  • @Angew second template parameter sets comparator that would be used if you do not pass one to `std::set` constructor explicitly. As you do pass it, it does not really matter if you pass comparator as second template parameter or not. – Slava Jul 02 '14 at 17:23
  • @Slava Incorrect. The second template parameter controls the *type* of the comparator, the constructor parameter specifies the *instance* of the comparator to use. The type of that constructor parameter is the template parameter. – Angew is no longer proud of SO Jul 02 '14 at 18:18
1

Something as

#include <iostream>
#include <set>

template <class T = double> class EpsCompare {
  public:
    EpsCompare(T input_eps) : _eps(input_eps) {};
    bool operator() (const T & element1, const T & element2) const {
       return ( round(element1*_eps) < round(element2*_eps) );
    }
    T _eps;
};


int main() 
{
    std::set<double, EpsCompare<double>> myEpsSet(EpsCompare<double>(1e-5) );

    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

If you want to pass eps as parameter to std::set you have to make it part of template. You cannot use double as template parameter, so one of the solution to have power as template parameter:

template <class T, int power > class EpsCompare {
  public:
    EpsCompare() { eps = pow( 10, power ); }
    bool operator() (const T & element1, const T & element2) const {
       return ( round(element1*eps) < round(element2*eps) );
    }
  private:
       T eps;
};

std::set<double, EpsCompare<double,-5>> myEpsSet;

If you want to pass eps as parameter to EpsCompare constructor, you make it runtime so you cannot use it in template and pass your comparator to std::set:

std::set<double,EpsCompare<double>> myEpsSet( EpsCompare<double>( 1e-5 ) );

will be sufficient.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • I had tried a similar solution before, but found this an unsatisfactory workaround considering that what I really want to pass is an interval size (might be 1.5 or something as well), not always just the number of digits... – urps Jul 02 '14 at 11:31
  • Then you have to pass it at runtime, unless you find a way to specify it as multiple ints for example. – Slava Jul 02 '14 at 18:27