std::is_floating_point returns false for float in some cases
Asked Answered
W

1

15

In some cases, see one example below, std::is_floating_point is returning false for 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;
}

Output from GCC

f
not floating point

In this example, one can see that typeid considers ::std::vector< float >()[::std::vector< float >().size()] as a float as it returns the correct name. One can also check that typeid(decltype(::std::vector< float >()[::std::vector< float >().size()])) == typeid(flat) returns true. However, std::is_floating_point is returning false. Why? Is that a bug from C++?

FYI, I checked with both GCC and VisualStudio. In this example, I used std::vector, but one can also try with other libraries, such as Eigen.

Wiggle answered 12/9, 2018 at 9:3 Comment(7)
You're lucky decltype is a compile-time feature, as std::vector< float >() creates an empty vector where even index 0 is out of bounds and otherwise lead to undefined behavior.Kelseykelsi
It is not relevant, I guess, since I am not using the created std::vector. And I used std::vector in the example for everyone to be able to try, I am using other libraries in my code.Calamite
@Someprogrammerdude Yup, there should be no UB in unevaluated operandsItemize
Like I said, in this case it's okay, but you should really be careful.Kelseykelsi
There's declval to be really safe. Doesn't even require a default ctor.Fabrianna
@Fabrianna In fact does not even require any accessible c'tor (in particular, it can be used with abstract types like std::istream).Lait
I asked similar question recently... #51897441 Basically for some reason type traits are dumb, and dont know/want to remove referenceKimball
L
29

There is no bug, and std::is_floating_point is giving you the right answer.

vector<float>[n] doesn't give you a float; it gives you a float&.

typeid ignores this for convenience but, as more "powerful" tools, decltype and std::is_floating_point do not.

You can use std::remove_reference to fix this:

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

You might also consider std::decay.

You don't need decltype anyway as containers have handy type aliases for times like this.

Here's what I'd do:

#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

Live demo

Lancet answered 12/9, 2018 at 9:5 Comment(7)
@Fred You don't need return in main, as the live demo shows, and I deliberately removed it for brevity as it is pointless. Thanks for the suggestion though.Lancet
True, clang-tidy doesn't complains about it. In a regular non-void returning function, not having a return statement is undefined behavior.Dignify
IMO, it's still bad style to omit the return from main(), and I get the impression that the language only made it allowed because so many people were using said bad style and it wanted to legitimise them for some reason.Chock
@Chock But in an example like this it is literally, completely, 100% pointless. I don't write code that doesn't do anything!Lancet
These "type of reference" discussions are perfect for language lawyers. Technically, the type of the expression is float, but the value category of the expression is lvalue. decltype( expr ) takes the type of expr and optionally adds a reference qualifier to it (i.e. if the value category of expr is lvalue). If decltype( expr ) simply gave the type of the expression, then the type trait instance would indeed evaluate to a true type. Not sure if even this is 100% correct.Lait
@Arne: I think you're correct. I do try to ignore these rules wherever possible because they give me a headache and just seem so arcane in a sense ;) though they are constructed so as to produce the "expected" outcome in precisely situations like this, so there is certainly logic thereLancet
(By this I mean that I've deliberately abstracted such things away for this answer, but indeed we could look at it in a more scholarly fashion if we were to be lawyer-like about it rather than practical)Lancet

© 2022 - 2024 — McMap. All rights reserved.