c++templatesc++20c++-concepts

Constrained template function never gets called


I have two template member functions that should be called based on meeting a constraint (being derived from a specific type)

I have the general case template function called addScheme defined as follows:

template <typename Field>
template <typename Scheme>
void TransportEquation<Field>::addScheme(Scheme&& scheme) {
    if (scheme.needsCorrection()) {
        _n_corrected_schemes++;
    }

    _schemes.emplace_back(std::make_shared<Scheme>(std::forward<Scheme>(scheme)));
}

I added another two functions, for two specific cases.

The first function I have no issues with, the template type Diffusion should be inherited from IDiffusion:

template <typename Field>
template <typename Diffusion>
requires std::derived_from<Diffusion, IDiffusion>
void TransportEquation<Field>::addScheme(Diffusion&& diffusion) {
    if (diffusion.needsCorrection()) {
        _n_corrected_schemes++;
    }

    auto diff_scheme = std::make_shared<Diffusion>(std::forward<Diffusion>(diffusion));
    _diff_scheme = diff_scheme;
    _schemes.emplace_back(diff_scheme);
}

My problem is with the other one, in which the scheme should be derived from IConvection<G> where G is another template type:

template <typename Field>
template <typename Convection, typename G>
requires std::derived_from<Convection, scheme::convection::IConvection<G>>
void TransportEquation<Field>::addScheme(Convection&& convection) {
    if (convection.needsCorrection()) {
        _n_corrected_schemes++;
    }

    auto conv_scheme = std::make_shared<Convection>(std::forward<Convection>(convection));
    _conv_scheme = conv_scheme;
    _schemes.emplace_back(conv_scheme);
}

This function never gets called, and the general one addScheme(Scheme&& scheme) always gets called instead. I don't know what is wrong with the function definition and why the constraint is never met?

In case it's useful, this is how I declare IConvection:

template <typename GradScheme = gradient::LeastSquares>
class IConvection {
// ...
};

and this is a derived type, that never matches the constrain of addScheme(Convection&&) and addScheme(Scheme&&) gets called instead:

template <typename G = gradient::LeastSquares>
class Upwind : public IConvection<G> {}

Solution

  • You don't have anything specifying G, so it isn't deduced, so the template is never matched.

    You could add a type alias to IConvection

    template <typename GradScheme = gradient::LeastSquares>
    class IConvection {
    // ...
        public:
        using gradient_scheme = GradScheme;
    };
    

    And then look for that in your requires clause. Any type that lacks a type alias gradient_scheme doesn't match this template, it isn't a hard error.

    template <typename Field>
    template <typename Convection>
    requires std::derived_from<Convection, scheme::convection::IConvection<typename Convection::gradient_scheme>>
    void TransportEquation<Field>::addScheme(Convection&& convection);