Search code examples
c++gccc++14type-traitsboost-hana

std::is_arithmetic returns false for int type inside generic lambda: Undefined behavior?


Consider:

#include <iostream>
#include <typeinfo>
#include <type_traits>

#include <cxxabi.h>

#include <boost/hana.hpp>
namespace hana = boost::hana;

struct Person {
  BOOST_HANA_DEFINE_STRUCT(Person,
    (std::string, name),
    (int, age)
  );
};

template<typename T>
void stringify(const T& v) {
    hana::for_each(hana::accessors<T>(), [&v](auto a) {
        // Here I'm printing the demangled type, just to make sure it is actually the type I'm thinking it is.
        std::cout << abi::__cxa_demangle(typeid(decltype(hana::second(a)(v)){}).name(), 0, 0, 0);

        // If the value is arithmetic, "quote" should be an empty string. Else, it should be an actual quote.
        // UNEXPECTED BEHAVIOR IS HERE
        std::string quote{(std::is_arithmetic<decltype(hana::second(a)(v))>::value?"":"\"")};

        // Finally do what we're here for.
        std::cout << " " << hana::first(a).c_str() << " = " << quote << hana::second(a)(v) << quote << "\n";
    });
}

int main() {
    Person john;
    john.name = "John Doe";
    john.age = 42;
    stringify(john);
}

See it live

Output:

std::__cxx11::basic_string</*...*/> name = "John Doe"
int age = "42"

I'm trying to use std::is_arithmetic to tell if I'm dealing with a number instead of some other non-arithmetic type, and print (or not) a quote accordingly.

But for some reason, the value being "returned" (through ::value member) is false, even though I'm passing an int(I make sure I'm doing that right by first printing the demangled type using gcc's cxxabi.h)

As you can see from the output, this causes the int to be printed with quotes.

My question(s) is: why is it returning false? Does this have anything to do with the generic lambda? Can I fix it?

I'm actually testing this directly on Coliru, so you can assume whatever gcc version used there (Currently 6.3.0).


Solution

  • Your problem is that in spite of the type typeid returns (int) your actual type inside the lambda is int const& and is_arithmetic is not specialized for that exact type. You can get the type you actually want with std::decay or a combination of std::remove_const and std::remove_reference.