Я пытаюсь понять универсальные ссылки и std::enable_if
лучше, но я немного застрял в том, что здесь происходит в моем коде.
Во-первых, я заметил, что люди, кажется,используйте std::enable_if
двумя различными способами:
template<typename T, std::enable_if<condition, T>::type* = nullptr>
или что-то подобное.
template<typename T> std::enable_if_t<condition, T> myfunc() {...}
или что-то подобное.
Я понимаю, что происходит во втором, но я не понимаю, почему кто-то будет использовать первый.Чего это добивается, кроме добавления еще одного параметра в шаблон?Это вещь SFINAE?
Я также застрял на универсальных ссылках при использовании enable_if
.Вот мой код и результаты, которые я получаю.Обратите внимание, что я использую код печати типа Говарда Хиннанта из " Возможно ли напечатать тип переменной в стандарте C ++? ", который я здесь опущу для краткости.
В любом случае,функция conditionless
, кажется, работает нормально со всеми.
Я очень запутался в is_integral
и decay
, которые вы можете увидеть в начале main
.Я получаю вывод:
true: unsigned long
false: unsigned long
false: unsigned long
false: unsigned long
, и я понятия не имею, почему последние три являются ложными.
Тогда у меня есть проблемы (отмеченные 1 и 2 висточник ниже), где при использовании enable_if
любым из двух упомянутых выше способов они отказываются компилироваться, принимая lvalue целочисленного типа или типа с плавающей запятой.
Заголовки и тип печатного кода опущены для краткости:
template<typename T>
void conditionless(T&& val) {
std::cout << "conditionless(" << val << ")\n";
}
template<typename T, typename std::enable_if<std::is_integral_v<T>, T>::type* = nullptr>
void outputIntType(T&& val) {
std::cout << "outputIntType(" << val << ")\n";
}
template<typename T>
typename std::enable_if_t<std::is_floating_point_v<T>>
outputFloatType(T&& val) {
std::cout << "outputFloatType(" << val << ")\n";
}
int main() {
size_t sz = 1;
size_t &ref = sz;
// All of these report as having type "unsigned long", but for some reason, the first reports true for is_integral, and
// the other three report false.
std::cout << std::boolalpha << std::is_integral_v<decltype(sz)> << ": " << type_name<decltype(sz)>() << '\n';
std::cout << std::boolalpha << std::is_integral_v<std::decay<decltype(sz)>> << ": " << type_name<std::decay<decltype(sz)>::type>() << '\n';
std::cout << std::boolalpha << std::is_integral_v<decltype(ref)> << ": " << type_name<decltype(sz)>() << '\n';
std::cout << std::boolalpha << std::is_integral_v<std::decay<decltype(ref)>> << ": " << type_name<std::decay<decltype(ref)>::type>() <<'\n';
// This works fine.
conditionless(sz);
conditionless(2UL);
conditionless(2L + 1);
// ******* 1 *******
// This fails and claims no matching function call to outputIntType(size_t&)
// template argument deduction / substitution failed:
// error: no type named 'type' in 'struct std::enable_if<false, long unisgned int&>'
// I'm particularly confused about why the is_integral evaluates to false.
//outputIntType(sz);
// These work fine.
outputIntType(2UL);
outputIntType(2L + 1);
double pi = 3.1415926535;
// These work fine.
conditionless(pi);
conditionless(2 * pi);
conditionless(0.00000001);
// ******* 2 *******
// This fails as well:
// main.cpp: In function 'int main()':
// error: no matching function for call to 'outputFloatType(double&)'
// note: candidate: 'template<class T> std::enable_if_t<is_floating_point_v<T> > outputFloatType(T&&)'
// template argument deduction/substitution failed:
// outputFloatType(pi);
// These work fine.
outputFloatType(2 * pi);
outputFloatType(0.00000001);
}
Любое понимание того, что кто-либо может дать мне представление о двух разных вариантах использования enable_if
и почему мой код с enable_if
отказывается принимать lvalues, будет очень признательно.