What syntax I want to achieve on user side:
double a(1.), b(2.), deps(.1);
bool res1 = compare<double>()(a, b); // works with default eps
bool res2 = compare<double, &deps>()(a, b); // works with explicitly provided eps
float c(1.), d(1.). feps(.1);
bool res3 = compare<float>()(c, d); // don't have default eps - must not compile
bool res4 = compare<float, &feps>()(c, d); // works only with provided eps
What implementation for this I have now (not working because default parameters for partial specialization are not allowed):
extern double eps_double; // somewhere defined and initialized
template<typename T, const T* eps>
struct compare { // actually inherits std::binary_function
bool operator()(const T& t1, const T& t2) {
return t1 < t2 - *eps;
}
};
template<const double* eps = &eps_double>
struct compare<double, eps> { // the same as in default implementation
};
I've tried with enable_if and wrapper classes that have static members, but static members can't be assigned to extern variables;
UPDATE: The actual problem is name equality for general struct and specialized struct. I don't know how to make it work without renaming:
// treats all explicitly passed eps and don't need default parameter
template<typename T, const T* eps>
struct compare_eps { // need another name!
bool operator()(const T& t1, const T& t2) {
return t1 < t2 - *eps;
}
};
// don't need default parameter either
// because we always know what eps to use for a concrete type
template<typename T>
struct compare {
// define nothing -> will not compile on types we don't have specialization for
};
template<>
struct compare<double> {
// use here eps_double hardcoded
};
I don't know why you think that this make sense
compare<double, deps>
You cannot make this work: Template arguments cannot be values of type double
(they can be an lvalue of type double
, but your template requires the address of a double
, so that's off).
You can use function templates to make your syntax work
extern double eps_double;
template<typename T>
types::compare<T, &eps_double> compare(
typename enable_if<is_same<T, double>>::type * = 0
) {
return types::compare<T, &eps_double>();
}
template<typename T, const T *eps>
types::compare<T, eps> compare() {
return types::compare<T, eps>();
}
Alternatively, you can use class templates if you are up for some ugly hacks
template<typename T, const T* eps = &eps_double>
struct compare {
bool operator()(const T& t1, const T& t2) {
return t1 < t2 - *eps;
}
};
The default argument will not be used if you provide both arguments. If you provide only <double>
, the default argument will be used and will work. If you only provide <float>
, the default argument will be used too, but will not work.