одноплодной класс предоставляет этот метод для клиентов, чтобы получить экземпляр: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?
Благодарим за быстрый ответ! Это действительно кажется более ясным с перегрузкой. Но почему вы используете 'class U = T', а затем используете' U'? Это связано с разрешением перегрузки? – Tarc
@Tarc SFINAE применяется только в непосредственном контексте. Если вы сделали 'is_default_constructible :: value', где' T' не является параметром шаблона, который сразу же будет оцениваться, и вы получите жесткую ошибку. –
Barry