1
#include <cinttypes>
#include <type_traits>

template<typename Id, typename Value>
class sparse_set {

  static_assert(std::is_integral_v<Id>, "");  (1)
  static_assert(std::is_unsigned_v<Id>, "");

  Value& operator[](Id id);

  void push_back(const Value& value);
 
  // class implementation left out

};

class entity {

public:
  explicit entity(std::uint32_t id) : _id(id) {}
  ~entity() = default;

  std::uint32_t id() const {
    return _id;
  }

  operator std::uint32_t() const {  (2)
    return _id;
  }

private:
  std::uint32_t _id;

}; // class entity

int main() {
    const auto e = entity{2};

    auto set = sparse_set<entity, int>{};

    set.push_back(0);
    set.push_back(1);
    set.push_back(2);
    set.push_back(3);

    auto i = set[e];  (3)
    
    return 0;
}

I am trying to use a class with a conversion operator to std::uint32_t (2) as an index into a container class (3). Accessing an element with an instance of that class works and i get the right element.

But testing the class with a static_assert and std::is_unsigned_v and std::is_integral_v results in an assertion failure.

I need assertions to make sure Id can be used as an index.

When I static_assert with std::uint32_t everything works so I would expect the conversion operator to work aswell.

Symlink
  • 383
  • 2
  • 12
  • 1
    Is `entity` (Id) an integral type? I don't think so. – vahancho Sep 21 '21 at 10:55
  • No, `static_assert(std::is_integral_v);` would complain. "_Provides the member constant `value` which is equal to `true`, if `T` is the type `bool`, `char`, `char8_t` (since C++20), `char16_t`, `char32_t`, `wchar_t`, `short`, `int`, `long`, `long long`, or any implementation-defined extended integer types, including any signed, unsigned, and cv-qualified variants. Otherwise, value is equal to `false`._". Also note: "_The behavior of a program that adds specializations for `is_integral` or `is_integral_v` (since C++17) is undefined._" – Ted Lyngmo Sep 21 '21 at 10:58
  • your code has several errors not related to the question. Please fix them and also add the necessary includes – 463035818_is_not_an_ai Sep 21 '21 at 11:03

2 Answers2

4

entity certainly is not an integral type. It can be converted to a type that can be used as index and thats what you require:

#include <type_traits>

template<typename Id, typename Value>
class sparse_set {

  static_assert(std::is_convertible_v<Id,size_t>, "");

  // class implementation left out

};
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
2

std::is_unsigned and std::is_integral only works for primitive types, it doesn't work for classes, even if they are implicitly convertible to a type they support. You can solve this by two ways:

  1. Create your own trait:

    #include <type_traits>
    
    // ...
    
    using uint16 = std::uint16_t;
    using uint32 = std::uint32_t;
    
    class entity { /* ... */ };
    
    template <typename T>
    struct is_valid_index : std::is_unsigned<T> {};
    template <>
    struct is_valid_index<entity> : std::true_type {};
    
    template <typename T>
    constexpr auto is_valid_index_v = is_valid_index<T>::value;
    
    template<typename Id, typename Value>
    class sparse_set {
        static_assert(is_valid_index_v<Id>, "");
        // ...
    };
    
    // ...
    
  2. Remove std::is_unsigned completely and use std::is_convertible instead:

    #include <type_traits>
    
    // ...
    
    using uint16 = std::uint16_t;
    using uint32 = std::uint32_t;
    
    class entity { /* ... */ };
    
    template<typename Id, typename Value>
    class sparse_set {
        static_assert(std::is_convertible_v<Id, uint32>, "");
        // ...
    };
    
    // ...
    
Ruks
  • 3,886
  • 1
  • 10
  • 22