简体   繁体   中英

Default parameter for partial specialization

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.

You need to change the structure which owns the comparison operator such that you can specialize the outer template, see: http://ideone.com/xqtjz

the code is:

extern double eps_double; // somewhere defined and initialized
extern double deps;  // NOTE: you have to make these extern a well, else cannot work
extern float feps;

template<typename T>
struct compare {
  // this internal structure now has the operator()
  template <const T* eps>
  struct it
  {
    bool operator()(const T& t1, const T& t2) const {
      return t1 < t2 - *eps;
    }
  };
};

// specialize compare for double
template<>
struct compare<double>
{
  // here you can provide the default
  template<const double* eps=&eps_double>
  struct it
  {
    bool operator()(const double& t1, const double& t2)
    {
      return t1 < t2 - *eps;
    }
  };
};

int main(void)
{
  double a(1.), b(2.);
  bool res1 = compare<double>::it<>()(a, b);        // works with default eps
  bool res2 = compare<double>::it<&deps>()(a, b);   // works with explicitly provided eps
  float c(1.), d(1.);
  bool res3 = compare<float>::it<>()(c, d);  // don't have default eps - will not compile
  bool res4 = compare<float>::it<&feps>()(c, d);   // works only with provided eps
}

I would go with traits-like approach:

template<class T>
struct DefaultEps;

template<>
struct DefaultEps<double>
{
  static const double eps = 4.0;
};

// may or may not be needed in .cpp
// const double DefaultEps<double>::eps;

template<>
struct DefaultEps<float>
{
  static const float eps = 4.0;
};

// may or may not be needed in .cpp
// const float DefaultEps<float>::eps;

template<class T, class E = DefaultEps<T> >
struct Compare
{
  bool operator()(T const &t1, T const &t2)
  {
    return(t1 < t2 - E::eps);
  }
};

and then when specific epsilon is needed:

struct SpecificEps
{
  static const float eps = 4.0;
};

// may or may not be needed in .cpp
// const float SpecificEps::eps;

and use it:

Compare<float, SpecificEps>()(...);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM