4

I like NVI idiom.

But sometimes I want to reduce vftable cost from NVI idiom. Then I tried to apply CRTP to NVI as following.

template<typename E>
class unary_interface {
public:
    virtual ~unary_interface() = default;

public:
    double operator()(const double x) const
    {
        return static_cast<const E&>(*this).apply(x);   //occurs compile error!!!
    }
};

class square : public unary_interface<square> {
private:
    double apply(const double x) const
    {
        return x * x;
    }
};

But this code occurs compile error.

If I change apply function in private field to public, encapsulation is broken. I got an idea that opaque alias solve this problem as following.

template<typename E>
class unary_interface {
public:
    virtual ~unary_interface() = default;

protected:
    class input_type {
    public:
        explicit input_type(const double x) : _x(x) {}
        operator double() const
        {
            return _x;
        }
    private:
        const double _x;
    };

public:
    double operator()(const double x) const
    {
        return static_cast<const E&>(*this).apply(input_type(x));
    }
};

class square : public unary_interface<square> {
    using base_type = unary_interface<square>;
public:
    double apply(const base_type::input_type& d) const
    {
        const double x = static_cast<const double>(d);
        return x * x;
    }
};

This design keeps to disable to access apply function except from operator() of unary_interface.

At first glance "apply function" is exposed to user code, but apply function is accept only protected opaque alias type that is defined on unary_interface in protected field. I think that this combination is very good and reduce virtual function cost with keeping strong encapsulation.

Does this idea has any defect that I can not find out, and do you have a specific name for this design?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Yusuke Mori
  • 151
  • 1
  • 8

1 Answers1

3

But this code occurs compile error.
...
At first glance "apply function" is exposed to user code, but apply function is accept only protected opaque alias type that is defined on unary_interface in protected field. I think that this combination is very good and reduce virtual function cost with keeping strong encapsulation.

You can easily solve your strong encapsulation dilemma using friend (and in that case it won't come with any negative consequences):

class square : public unary_interface<square> {
    friend class unary_interface<square>; // <<<
    double apply(const double x) const // <<< Keep apply() private
    {
        return x * x;
    }
};

No need to deviate using the protected input_type.

See Live Demo please.

Does this idea has any defect that I can not find out, and do you have a specific name for this design?

Well, the defect is over-complicating the situation and presenting the final user a public function they cannot actually use.

I don't know if that anti-pattern already was identified and named. That somehow misuses the public interface area by declaring promises that cannot effectively be used at the public API.

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • Thanks for your reply. I agree that complicated design is bad. At first, I got idea of using friend. I wrongly thought that friend is always deprecated, but I see that friend is efficient tool in the specific situation thanks to you. – Yusuke Mori Feb 25 '17 at 06:34
  • @YusukeMori Glad to be helpful. You may need to find a better title for that question. But in general it's considerable and useful. There's a different case when you expose `private` internal class _handles_, that can be used with `auto` from the `public` ´API. – πάντα ῥεῖ Feb 25 '17 at 06:48