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.

Clèm
  • 424
  • 3
  • 14
  • 1
    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*](https://en.wikipedia.org/wiki/Undefined_behavior). – Some programmer dude Sep 12 '18 at 09:07
  • 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. – Clèm Sep 12 '18 at 09:14
  • 1
    @Someprogrammerdude Yup, [there should be no UB in unevaluated operands](https://stackoverflow.com/a/28723577/1708801) – Shafik Yaghmour Sep 12 '18 at 09:20
  • Like I said, in this case it's okay, but you should really be careful. – Some programmer dude Sep 12 '18 at 09:20
  • 4
    There's `declval` to be really safe. Doesn't even require a default ctor. – MSalters Sep 12 '18 at 11:13
  • 2
    @MSalters In fact does not even require any accessible c'tor (in particular, it can be used with abstract types like `std::istream`). – Arne Vogel Sep 12 '18 at 11:26
  • I asked similar question recently... https://stackoverflow.com/questions/51896941/is-there-a-reason-why-numeric-limits-do-not-work-on-reference-types Basically for some reason type traits are dumb, and dont know/want to remove reference – NoSenseEtAl Sep 12 '18 at 12:57

1 Answers1

30

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

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 2
    @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. – Lightness Races in Orbit Sep 12 '18 at 09:48
  • True, clang-tidy doesn't complains about it. In a regular non-void returning function, not having a return statement is undefined behavior. – Frederik De Ruyck Sep 12 '18 at 10:03
  • 2
    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. – underscore_d Sep 12 '18 at 10:11
  • 1
    @underscore_d But in an example like this it is literally, completely, 100% pointless. I don't write code that doesn't do anything! – Lightness Races in Orbit Sep 12 '18 at 10:14
  • 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. – Arne Vogel Sep 12 '18 at 11:30
  • 2
    @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 there – Lightness Races in Orbit Sep 12 '18 at 11:35
  • (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) – Lightness Races in Orbit Sep 12 '18 at 11:42