2016-12-21 3 views
3

одноплодной класс предоставляет этот метод для клиентов, чтобы получить экземпляр:VARIADIC специализация шаблона, станд :: enable_if, SFINAE

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<> в качестве параметра или параметра шаблона вместо типа возврата?

Как работает компилятор? Когда это была простая простая специализация шаблона (в Wrong Version) Я предполагаю, что компилятор понял, что более специализированная версия была лучше для кодового вызова GetInstance() без параметров (T - класс со стандартным конструктором).

Для версии с enable_if<> компилятор начинает думать, что было бы лучше использовать более специализированную версию, но тогда код был плохо сформирован. Итак, он возвращается к общей версии? Это также называется SFINAE?

ответ

4

Удобное эмпирическое правило: не специализируется, перегрузка.

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 пуст и является предпочтительным.


Примечание. Этот дизайн кажется подозрительным.

+0

Благодарим за быстрый ответ! Это действительно кажется более ясным с перегрузкой. Но почему вы используете 'class U = T', а затем используете' U'? Это связано с разрешением перегрузки? – Tarc

+0

@Tarc SFINAE применяется только в непосредственном контексте. Если вы сделали 'is_default_constructible :: value', где' T' не является параметром шаблона, который сразу же будет оцениваться, и вы получите жесткую ошибку. – Barry

Смежные вопросы