c++idiomsnon-virtual-interface

Under the NVI idiom, why can't the virtual function be public?


C++ private and protected virtual method and Is there any valid reason for not using public virtual methods? are talking about Non-Virtual Interface (NVI) and non-public virtual function and their symbiosis. Scott Meyers also says in Effective C++ that

Sometimes a virtual function even has to be public, but then the NVI idiom can't really be applied.

What I failed to see is why NVI requires that the implementation specific virtual function(s) be non-public? From Herb Sutter's article Virtuality, it is saying that it is a good practice to follow, e.g., it is good to separate public (client) interface from the implementation details (the non-public interface). What I want to know is if there is any language feature I missed that semantically prevents NVI being applied if such virtual functions are declared public?

For example:

class Engine
{
public:
    void SetState( int var, bool val );
    {   SetStateBool( int var, bool val ); }

    void SetState( int var, int val );
    {   SetStateInt( int var, int val ); }
private:
    virtual void SetStateBool(int var, bool val ) = 0;    
    virtual void SetStateInt(int var, int val ) = 0;    
};

What are the effects if I put SetStateBool and SetStateInt in public section of the class definition?


Solution

  • TLDR: You can, but you shouldn't.

    Suppose you want to make sure that every call to the public interface is properly logged (financial services legal requirement e.g.)

    class Engine
    {
    public:
        void SetState( int var, bool val );
        {  
            logToFile(); 
            SetStateBool( int var, bool val ); 
        }
    
        void SetState( int var, int val );
        {   
            logToFile();
            SetStateInt( int var, int val ); 
        }
    private:
        virtual void SetStateBool(int var, bool val ) = 0;    
        virtual void SetStateInt(int var, int val ) = 0;
        void logToFile();    
    };
    

    Because the public interface is non-virtual, all derived classes automatically have logging as well. If instead you would have made SetStateBool and SetStateInt public, you could not have enforced logging for all derived classes.

    So the recommendation to use the NVI idiom is not a syntactic requirement, but it's a tool to enforce the base class semantics (logging, or caching) on all derived classes.