0

I'm trying to get size of a std::tuple at compile time using a constexpr function.

I write this, which I thought might be easy to use and could give a clear error hint:

template<typename... TArgs>
constexpr int get_tuple_size(const std::tuple<TArgs...>&) { return sizeof...(TArgs); }

Soon I realized I don't want to construct a std::tuple really, so I tried this:

#include <cstdio>
#include <tuple>
template<typename... TArgs>
constexpr int get_tuple_size(std::tuple<TArgs...>&&) { return sizeof...(TArgs); }
template<typename T>
constexpr T&& make_rvalue() {
    return ((T&&)*(T*)0);
}
int main() {
    printf("%d\n", get_tuple_size(make_rvalue<std::tuple<int, double, float&>>()));
    return 0;
}

EDIT: this code can work in Clang 7.0.1 GCC 9.2.0 VS2019

This code meets all my requirements, but is it really legal?

I mean, when I write ((T&&)*(T*)0) in a non-constexpr function, it gave an error.

error C4854: binding dereferenced null pointer to reference has undefined behavior

By the way, is there any better solution to solve it?

I have tried this:

    template<typename T>
    struct is_tuple {
        static constexpr bool value = false;
    };
    template<typename... TArgs>
    struct is_tuple<std::tuple<TArgs...>> {
        static constexpr bool value = true;
    };
    template<typename T>
    constexpr bool is_tuple_v = is_tuple<T>::value;
    template<typename T>
    struct sizeof_tuple {
        static_assert(is_tuple_v<T>, "==========>sizeof_tuple input type isn't std::tuple");
    };
    template<typename... TArgs>
    struct sizeof_tuple<std::tuple<TArgs...>> {
        static constexpr size_t value = sizeof...(TArgs);
    };
    template<typename T>
    constexpr size_t sizeof_tuple_v = sizeof_tuple<T>::value;

But if you input an wrong type, the error hint won't point to where you referenced sizeof_tuple_v<T> but the declaration itself.

victor12369
  • 143
  • 10
  • why not use [`std::tuple_size`](https://en.cppreference.com/w/cpp/utility/tuple/tuple_size)? – Alan Birtles Mar 24 '21 at 08:40
  • Thanks! That's really a good idea @AlanBirtles , but that's not my main question. – victor12369 Mar 24 '21 at 08:50
  • If you don't want to construct a tuple, you should pass the type as a template parameter, intead of relying on a function with an argument. `get_tuple_size(make_rvalue>()))` is just a worse version of `get_tuple_size>()`, no? – super Mar 24 '21 at 09:10
  • C++ does not have null references. In general, `*(T*)0` gives your program undefined behavior. – Pete Becker Mar 24 '21 at 14:16
  • @Pete Becker, but you can have references to pointers (that can be nullptr).The dereferencing itself is UB then, that's right. – Secundi Mar 25 '21 at 09:12
  • Edit: AFAIK, the standard is not that specific about UB in terms of nullptr dereferencing. It's quite clear about UB when accessing underlying data via dereferencing but that's not the case here. – Secundi Mar 25 '21 at 09:19
  • For the reference question portion, also see https://stackoverflow.com/questions/2727834/c-standard-dereferencing-null-pointer-to-get-a-reference – Secundi Mar 25 '21 at 09:31
  • 1
    @Secundi -- "[ Note: In particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by indirection through a null pointer, which causes undefined behavior. As described in 12.2.4, a reference cannot be bound directly to a bit-field. — end note ]" [dcl.ref]/5. – Pete Becker Mar 25 '21 at 12:18
  • Thank you Peter, I was not aware of this special note. – Secundi Mar 25 '21 at 14:17

0 Answers0