【问题标题】:Variadic template specialization, std::enable_if, SFINAE可变模板特化,std::enable_if,SFINAE
【发布时间】:2016-12-21 18:38:30
【问题描述】:

单例类公开此方法供客户端获取实例:

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

    return * m_instance;
}

但是对于没有默认构造函数的类,总是传递参数很烦人。最好是在实例创建完成后允许用户调用:

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

但对于具有默认构造函数的类,将使用这种特化代替更通用的特化。我希望只有在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&lt;&gt; 作为参数或模板参数而不是返回类型吗?

编译器在这里是如何工作的?当它只是简单的模板专业化(在错误版本中)时,我猜编译器意识到更专业的版本更适合在没有参数的情况下调用GetInstance()(T 是一个类默认构造函数)。

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

【问题讨论】:

    标签: c++ templates template-specialization sfinae


    【解决方案1】:

    方便的经验法则:不要专门化,超载。

    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 为空且首选时,第二个更专业。


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

    【讨论】:

    • 感谢您的快速回答!过载确实看起来更清楚。但是为什么要使用class U=T,然后使用U?这是否与重载决议有关?
    • @Tarc SFINAE 仅适用于直接上下文。如果您执行了is_default_constructible&lt;T&gt;::value,其中T 不是模板参数,则会立即进行评估,您将得到一个硬错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多