可变参数模板专业化,std::enable_if,SFINAE

Variadic template specialization, std::enable_if, SFINAE

一个单例class公开了这个方法供客户端获取实例:

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

    return * m_instance;
}

但是对于没有默认构造函数的classes,总是传递参数是很烦人的。最好是,一旦创建了实例,允许用户调用:

auto & instance = GetInstance( );

起初我认为要让它工作,只需要专门化模板化方法,比如:

// 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;
}

但对于具有默认构造函数的 classes,将使用此特化 而不是 更通用的特化。我希望仅当 T 没有默认构造函数时才使用这种特化

所以我试着改变了一下:

// 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;
}

所以这行得通,但我对整个事情感到困惑。首先,我的处理方式是否正确?我不应该使用 enable_if<> 作为参数或模板参数而不是 return 类型吗?

这里的编译器是如何工作的?当它只是简单的模板专业化时(在 错误版本 中)我猜编译器意识到更专业的版本更适合不带参数调用 GetInstance() 的代码(T 是具有默认构造函数的 class。

对于带有 enable_if<> 的版本,编译器开始认为最好也使用更专业的版本,但代码格式不正确。所以它回退到通用版本?这也叫SFINAE吗?

实用的经验法则:不要专门化,超载。

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();

如果 T 是默认可构造的,则您只有一个可行的重载。如果不是,则当 Args 为空且首选时,第二个更专业。


注意。这种设计似乎很可疑。