c++templatesconstraintsfunction-templatesclass-template

How to constrain template methods to match class template parameters?


I have a following class:

template<typename Population, typename... Operators> class GA
{
public:
    template<typename Evaluator,
             typename std::enable_if_t<
             std::is_same_v<Evaluator, Evaluator<Operators...>>>>
  void EvaluatePopulation(Evaluator& evaluator)
  {
    evaluator.Evaluate();
  }
};

This doesn't compile, because of incorrect EvaluatePopulation declaration.

How do I specify that EvaluatePopulation method can be called only with Evaluator class that was initialized with the same Operators... as the GA class?


Solution

  • How do I specify that EvaluatePopulation method can be called only with Evaluator class that was initialised with the same Operators... as the GA class?

    The trick is to use a template template parameter so that you “unpack” the evaluator’s template arguments to match the GA’s pack. For example, you can write:

    template<typename Population, typename... Operators>
    class GA
    {
    public:
        template<template<typename...> class EvaluatorT>
        void EvaluatePopulation(EvaluatorT<Operators...>& evaluator)
        {
            evaluator.Evaluate();
        }
    };
    

    ((See live demo))


    Alternatively, using 's concepts, which gives clearer error messages:

    // Ensures Evaluator is instantiated with exactly Operators...
    template<typename T, typename... Args>
    constexpr bool is_evaluator_for_v = false;
    
    template<template<typename...> class EvaluatorT, typename... Ops>
    constexpr bool is_evaluator_for_v<EvaluatorT<Ops...>, Ops...> = true;
    
    template<typename Evaluator, typename... Operators>
    concept EvaluatorFor = is_evaluator_for_v<Evaluator, Operators...>;
    
    template<typename Population, typename... Operators> class GA 
    {
    public:
        
        template<typename Evaluator> requires EvaluatorFor<Evaluator, Operators...>
        void EvaluatePopulation(Evaluator& evaluator) 
        {
            evaluator.Evaluate();
        }
    };
    

    ((See live demo))