std::is_floating_point возвращает false для float в некоторых случаях

В некоторых случаях, см. пример ниже, std::is_floating_point возвращает false вместо float.

#include <iostream>
#include <type_traits>
#include <vector>

int main()
{
    ::std::cout << typeid(decltype(::std::vector< float >()[::std::vector< float >().size()])).name() << ::std::endl;
    if (::std::is_floating_point< decltype(::std::vector< float >()[::std::vector< float >().size()]) >::value)
    {
        ::std::cout << "floating point" << ::std::endl;
    }
    else
    {
        ::std::cout << "not floating point" << ::std::endl;
    }
    return 0;
}

Выход из GCC

f
not floating point

В этом примере видно, что typeid рассматривает ::std::vector< float >()[::std::vector< float >().size()] как float, поскольку возвращает правильное имя. Также можно проверить, что typeid(decltype(::std::vector< float >()[::std::vector< float >().size()])) == typeid(flat) возвращает true. Однако std::is_floating_point возвращает false. Почему? Это ошибка C++?

К вашему сведению, я проверил как с помощью GCC, так и с VisualStudio. В этом примере я использовал std::vector, но можно попробовать и другие библиотеки, например Eigen.


person Clèm    schedule 12.09.2018    source источник
comment
Вам повезло, что decltype — это функция времени компиляции, так как std::vector< float >() создает пустой вектор, где даже индекс 0 выходит за границы, и в противном случае приводит к неопределенное поведение.   -  person Some programmer dude    schedule 12.09.2018
comment
Думаю, это не имеет значения, поскольку я не использую созданный std::vector. И я использовал std::vector в примере, чтобы все могли попробовать, я использую другие библиотеки в своем коде.   -  person Clèm    schedule 12.09.2018
comment
@Someprogrammerdude Ага, в невычисленных операндах не должно быть UB   -  person Shafik Yaghmour    schedule 12.09.2018
comment
Как я уже сказал, в этом случае все в порядке, но вы действительно должны быть осторожны.   -  person Some programmer dude    schedule 12.09.2018
comment
Есть declval, чтобы быть в полной безопасности. Даже не требует ctor по умолчанию.   -  person MSalters    schedule 12.09.2018
comment
@MSalters На самом деле даже не требуется никакого доступного c'tor (в частности, его можно использовать с абстрактными типами, такими как std::istream).   -  person Arne Vogel    schedule 12.09.2018
comment
Недавно я задавал подобный вопрос... stackoverflow.com/questions/51896941/ В основном по какой-то причине черты типа глупы и не знают/не хотят удалять ссылку   -  person NoSenseEtAl    schedule 12.09.2018


Ответы (1)


Ошибки нет, и std::is_floating_point дает вам правильный ответ.

vector<float>[n] не дает вам float; это дает вам float&.

typeid игнорирует это для удобства, но, как более "мощные" инструменты, decltype и std::is_floating_point не надо.

Вы можете использовать std::remove_reference, чтобы исправить это:

if (::std::is_floating_point_v<std::remove_reference_t<
   decltype(::std::vector< float >()[::std::vector< float >().size()])
>>)

Вы также можете рассмотреть std::decay.

Вам все равно не нужен decltype, так как у контейнеров есть удобные псевдонимы типов для таких случаев.

Вот что я бы сделал:

#include <iostream>
#include <type_traits>
#include <vector>

int main()
{
    using V = std::vector<float>;

    ::std::cout << typeid(V::value_type).name() << '\n';
    if (::std::is_floating_point_v<V::value_type>)
        ::std::cout << "floating point\n";
    else
        ::std::cout << "not floating point\n";
}

// Output:
//   f
//   floating point

Текущая демонстрация

person Lightness Races in Orbit    schedule 12.09.2018
comment
@Fred Вам не нужно return в main, как показывает живая демонстрация, и я намеренно удалил его для краткости, поскольку это бессмысленно. Спасибо за предложение. - person Lightness Races in Orbit; 12.09.2018
comment
Правда, clang-tidy на это не жалуется. В обычной возвращающей функции, отличной от void, отсутствие оператора return является неопределенным поведением. - person Frederik De Ruyck; 12.09.2018
comment
ИМО, опускать return из main() по-прежнему плохой стиль, и у меня складывается впечатление, что язык позволил это сделать только потому, что так много людей использовали указанный плохой стиль, и по какой-то причине он хотел их узаконить. - person underscore_d; 12.09.2018
comment
@underscore_d Но в таком примере это буквально, совершенно, на 100% бессмысленно. Я не пишу код, который ничего не делает! - person Lightness Races in Orbit; 12.09.2018
comment
Этот тип справочных дискуссий идеально подходит для языковых юристов. Технически тип выражения — float, но категория значения выражения — lvalue. decltype( expr ) принимает тип expr и дополнительно добавляет к нему квалификатор ссылки (т. е. если категория значения expr равна lvalue ). Если бы decltype( выражение ) просто дало тип выражения, то экземпляр признака типа действительно оценивался бы как истинный тип. Не уверен, что даже это на 100% правильно. - person Arne Vogel; 12.09.2018
comment
@Arne: я думаю, ты прав. Я стараюсь игнорировать эти правила везде, где это возможно, потому что они вызывают у меня головную боль и в каком-то смысле кажутся такими загадочными ;) хотя они построены так, чтобы давать ожидаемый результат именно в таких ситуациях, так что в них определенно есть логика. - person Lightness Races in Orbit; 12.09.2018
comment
(Под этим я подразумеваю, что я намеренно абстрагировал такие вещи для этого ответа, но на самом деле мы могли бы взглянуть на него более научным образом, если бы мы относились к этому как к юристу, а не практично) - person Lightness Races in Orbit; 12.09.2018