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.

3
  • 1
    Is entity (Id) an integral type? I don't think so. Commented Sep 21, 2021 at 10:55
  • No, static_assert(std::is_integral_v<entity>); 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." Commented Sep 21, 2021 at 10:58
  • your code has several errors not related to the question. Please fix them and also add the necessary includes Commented Sep 21, 2021 at 11:03

2 Answers 2

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

};
Sign up to request clarification or add additional context in comments.

Comments

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>, "");
        // ...
    };
    
    // ...
    

1 Comment

isnt anything that is_unsigned also is_convertible_v<T,uint32> ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.