Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic template specialization, std::enable_if, SFINAE

A singleton class exposes this method for clients to obtain the instance:

template< typename ... Args >
static T & GetInstance( Args && ... args )
{
    if ( ! m_instance )
    {
        m_instance = new T( std::forward< Args >( args ) ... );
    }

    return * m_instance;
}

But for classes with no default constructors, it's annoying to allways pass the arguments. It would be better if, once the instance has already been created, to allow users just to call:

auto & instance = GetInstance( );

At first I thought that for this to work all it was needed was to specialize the templated method, say with:

// Wrong Version
template< >
static T & GetInstance< >( )
{
    if ( ! instance )
    {
        throw std::runtime_error(
            "Tried to get instance of non initialized singleton with no"
            " default constructor." );
    }

    return * instance;
}

But for classes with a default constructor, this specialization would be used instead of the more general one. I would like that such specialization be used only if T has no default constructor.

So I tried to change it a bit:

// Right Version
template< >
static auto GetInstance< >( ) ->
    typename std::enable_if<
        ! std::is_default_constructible< T >::value , T & >::type 
{
    if ( ! instance )
    {
        throw std::runtime_error(
            "Tried to get instance of non initialized singleton with no"
            " default constructor." );
    }

    return * instance;
}

So this worked, but I'm confused about the whole thing. To begin with, is the way I've done it the proper way to handle it? Shouldn't I being using enable_if<> as a parameter or a template parameter instead of a return type?

How the compiler works here? When it was just plain simple template specialization (in the Wrong Version) I guess the compiler realized that the more specialized version was better for code calling GetInstance() with no parameters (T being a class with a default construtor).

For the version with the enable_if<>, the compiler begins thinking that it would be better also to use the more specialized version, but then the code was ill formed. So it falls back to the general version? Is this also called SFINAE?

like image 873
Tarc Avatar asked Dec 21 '25 15:12

Tarc


1 Answers

Handy rule of thumb: don't specialize, overload.

template <class... Args>
static T& GetInstance(Args&&... );

template <class U=T, std::enable_if_t<!std::is_default_constructible<U>::value, int> = 0>
static T& GetInstance();

If T is default constructible, you only have one viable overload. If it's not, the second is more specialized when Args is empty and is preferred.


Note. This design seems suspect.

like image 126
Barry Avatar answered Dec 23 '25 15:12

Barry



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!