#c #sfinae #void-t
#c #sfinae #void-t
Вопрос:
Извините за название, я не уверен в категории моего вопроса. Я пытаюсь выполнить is_incrementable с SFINAE. Это работает нормально, однако, когда я пытаюсь понять это более глубоко, и когда я удаляю void_t, фрагмент кода работает не так, как ожидалось.
Исходный код:
#include <iostream>
template< typename, typename = void >
struct is_incrementable : std::false_type { };
template< typename T >
struct is_incrementable<T,
std::void_t<decltype( std::declval<Tamp;>() )>
> : std::true_type { };
int main()
{
std::cout << is_incrementable<int>::value << std::endl; // prints 1
std::cout << is_incrementable<std::string>::value << std::endl; // prints 0
return 0;
}
i) is_incrementable<int>::value
вычисляет, к is_incrementable<int, void>::value
какому классу исходного шаблона относится и специализация. В этом случае компилятор выбирает специализированную версию, поэтому значение eguals равно 1.
Для строковой версии специализация завершается неудачно, запускается SFINAE, поэтому у нас есть только базовый шаблон. (значение равно 0)
ii) Когда я изменяю код и удаляю void_t
template< typename, typename = void >
struct is_incrementable : std::false_type { };
template< typename T >
struct is_incrementable<T,
decltype( std::declval<Tamp;>() ) // void_t is removed
> : std::true_type { };
int main()
{
std::cout << is_incrementable<int>::value << std::endl; // prints 0
std::cout << is_incrementable<std::string>::value << std::endl; // prints 0
return 0;
}
печатаются 0 и 0.
is_incrementable<int>::value
означает is_incrementable<int, void>::value
, что специализация
is_incrementable<int, int>::value
(Я думаю), поэтому мы используем базовый шаблон. Для строки специализация в любом случае завершается неудачно.
Мой вопрос: iii) Забавная часть. Если теперь я изменю первую строку, чтобы использовать int в качестве типа по умолчанию
#include <iostream>
template< typename, typename = int > // !!! int is used now
struct is_incrementable : std::false_type { };
template< typename T >
struct is_incrementable<T,
decltype( std::declval<Tamp;>() ) // void_t is removed
> : std::true_type { };
int main()
{
std::cout << is_incrementable<int>::value << std::endl; // prints 0
std::cout << is_incrementable<std::string>::value << std::endl; // prints 0
return 0;
}
затем снова печатаются 0 и 0.
Почему?
is_incrementable<int>::value
означает (я думаю) is_incrementable<int, int>::value
и
decltype( std::declval<Tamp;>() )
также должно быть int .
Поэтому я думаю, что компилятор должен использовать специализированную версию. (и 1 должен быть напечатан)
Если я удалю decltype( std::declval<Tamp;>() )
и запишу int
, тогда будут напечатаны 1 и 1 (которые являются ожидаемыми распечатками).
Не мог бы кто-нибудь объяснить мне, что происходит в iii) пожалуйста.
Ответ №1:
Когда T
есть int
, decltype( std::declval<Tamp;>() )
есть int amp;
, нет int
. Таким образом, чтобы получить результат, который вы ожидали, вы должны либо изменить это:
template< typename, typename = int >
struct is_incrementable : std::false_type { };
к этому:
template< typename, typename = int amp; >
struct is_incrementable : std::false_type { };
или измените это:
template< typename T >
struct is_incrementable<T,
decltype( std::declval<Tamp;>() )
> : std::true_type { };
к этому:
template< typename T >
struct is_incrementable<T,
std::remove_reference_t<
decltype( std::declval<Tamp;>() )
>
> : std::true_type { };
Комментарии:
1. Спасибо за ваш ответ, руах! Вы правы, оба решения, о которых вы упомянули, работают. Еще одна вещь, если я изменю строку decltype на «decltype( std::declval<T>() )» (поэтому я удалил amp; ), тогда он снова напечатает 0 и 0, независимо от первой строки («typename = int» или «typename = intamp;»)
2. @thamas,
std::declval<T>()
неверно сформирован при исходе изis_incrementable<int>::value
, т. Е. сT == int
помощью . Вы можете убедиться в этом, попытавшись скомпилироватьstd::declval<intamp;>();
vsstd::declval<int>();
.3. @thamas: чтобы объяснить комментарий Энлико:
требуется значение lvalue; то есть что-то вроде
3
недопустимо.4. Хорошо, итак, declval использует add_rvalue_reference , поэтому использование <int> вернет intamp;amp; , и его нельзя увеличить. Вам обоим большое спасибо!