std::map::size_type for a std::map whose value_type is its own size_type











up vote
4
down vote

favorite
2












I have a std::map<std::pair<std::string, std::string>, float> that is taking up too much memory, and in order to use less memory, I've decided to map the unique strings to integers (e.g., std::map<std::string, int>, where each new unique string is mapped to the current size() of the map), and use those integer value as pairwise keys to the map, (e.g., std::map<std::pair<int, int>, float>).



Instead of int, I want to use std::map::size_type:



using map_index = std::map::size_type;
std::pair<map_index, map_index> key;


Of course, this doesn't compile because I need to supply the argument list for the map:



vector.cc:14:19: error: invalid use of template-name `std::map' without an argument list
using map_index = std::map::size_type;


And this (in theory) is what I'm trying to achieve:



using map_index = std::map<std::string, map_index>::size_type;


which gives the following (expected) compiler error:



vector.cc:15:41: error: `map_index' was not declared in this scope
using map_index = std::map<std::string, map_index>::size_type;


What is the proper way to get the compiler to infer the correct value_type for a std::map whose value_type is its own size_type?










share|improve this question
























  • minor nitpick: I think you have the last sentence the wrong way around. To know what is size_type you first need to tell what is the value_type not the other way around. Once you know the type of the map, getting its size_type is trivial
    – user463035818
    Nov 22 at 12:44












  • @user463035818 The problem seems to be that the size_type is a part of value_type for the OP.
    – Some programmer dude
    Nov 22 at 12:46






  • 5




    You have a circular dependency. Why can't you use size_t (which is what size_type normally will be anyway)?
    – Some programmer dude
    Nov 22 at 12:47










  • can you explain why you want that? afaik map::size_type is just a typedef aliasing always the same type anyhow
    – user463035818
    Nov 22 at 12:47








  • 3




    std::map<K, V>::size_type is most likely completely independent of both K and V. If you really care, you can static_assert(std::is_same_v<Map::size_type, Map::mapped_type>, "Unexpected size_type")
    – Caleth
    Nov 22 at 12:59















up vote
4
down vote

favorite
2












I have a std::map<std::pair<std::string, std::string>, float> that is taking up too much memory, and in order to use less memory, I've decided to map the unique strings to integers (e.g., std::map<std::string, int>, where each new unique string is mapped to the current size() of the map), and use those integer value as pairwise keys to the map, (e.g., std::map<std::pair<int, int>, float>).



Instead of int, I want to use std::map::size_type:



using map_index = std::map::size_type;
std::pair<map_index, map_index> key;


Of course, this doesn't compile because I need to supply the argument list for the map:



vector.cc:14:19: error: invalid use of template-name `std::map' without an argument list
using map_index = std::map::size_type;


And this (in theory) is what I'm trying to achieve:



using map_index = std::map<std::string, map_index>::size_type;


which gives the following (expected) compiler error:



vector.cc:15:41: error: `map_index' was not declared in this scope
using map_index = std::map<std::string, map_index>::size_type;


What is the proper way to get the compiler to infer the correct value_type for a std::map whose value_type is its own size_type?










share|improve this question
























  • minor nitpick: I think you have the last sentence the wrong way around. To know what is size_type you first need to tell what is the value_type not the other way around. Once you know the type of the map, getting its size_type is trivial
    – user463035818
    Nov 22 at 12:44












  • @user463035818 The problem seems to be that the size_type is a part of value_type for the OP.
    – Some programmer dude
    Nov 22 at 12:46






  • 5




    You have a circular dependency. Why can't you use size_t (which is what size_type normally will be anyway)?
    – Some programmer dude
    Nov 22 at 12:47










  • can you explain why you want that? afaik map::size_type is just a typedef aliasing always the same type anyhow
    – user463035818
    Nov 22 at 12:47








  • 3




    std::map<K, V>::size_type is most likely completely independent of both K and V. If you really care, you can static_assert(std::is_same_v<Map::size_type, Map::mapped_type>, "Unexpected size_type")
    – Caleth
    Nov 22 at 12:59













up vote
4
down vote

favorite
2









up vote
4
down vote

favorite
2






2





I have a std::map<std::pair<std::string, std::string>, float> that is taking up too much memory, and in order to use less memory, I've decided to map the unique strings to integers (e.g., std::map<std::string, int>, where each new unique string is mapped to the current size() of the map), and use those integer value as pairwise keys to the map, (e.g., std::map<std::pair<int, int>, float>).



Instead of int, I want to use std::map::size_type:



using map_index = std::map::size_type;
std::pair<map_index, map_index> key;


Of course, this doesn't compile because I need to supply the argument list for the map:



vector.cc:14:19: error: invalid use of template-name `std::map' without an argument list
using map_index = std::map::size_type;


And this (in theory) is what I'm trying to achieve:



using map_index = std::map<std::string, map_index>::size_type;


which gives the following (expected) compiler error:



vector.cc:15:41: error: `map_index' was not declared in this scope
using map_index = std::map<std::string, map_index>::size_type;


What is the proper way to get the compiler to infer the correct value_type for a std::map whose value_type is its own size_type?










share|improve this question















I have a std::map<std::pair<std::string, std::string>, float> that is taking up too much memory, and in order to use less memory, I've decided to map the unique strings to integers (e.g., std::map<std::string, int>, where each new unique string is mapped to the current size() of the map), and use those integer value as pairwise keys to the map, (e.g., std::map<std::pair<int, int>, float>).



Instead of int, I want to use std::map::size_type:



using map_index = std::map::size_type;
std::pair<map_index, map_index> key;


Of course, this doesn't compile because I need to supply the argument list for the map:



vector.cc:14:19: error: invalid use of template-name `std::map' without an argument list
using map_index = std::map::size_type;


And this (in theory) is what I'm trying to achieve:



using map_index = std::map<std::string, map_index>::size_type;


which gives the following (expected) compiler error:



vector.cc:15:41: error: `map_index' was not declared in this scope
using map_index = std::map<std::string, map_index>::size_type;


What is the proper way to get the compiler to infer the correct value_type for a std::map whose value_type is its own size_type?







c++ c++11 stl stdmap size-type






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 22 at 13:53

























asked Nov 22 at 12:41









vallismortis

3,367103962




3,367103962












  • minor nitpick: I think you have the last sentence the wrong way around. To know what is size_type you first need to tell what is the value_type not the other way around. Once you know the type of the map, getting its size_type is trivial
    – user463035818
    Nov 22 at 12:44












  • @user463035818 The problem seems to be that the size_type is a part of value_type for the OP.
    – Some programmer dude
    Nov 22 at 12:46






  • 5




    You have a circular dependency. Why can't you use size_t (which is what size_type normally will be anyway)?
    – Some programmer dude
    Nov 22 at 12:47










  • can you explain why you want that? afaik map::size_type is just a typedef aliasing always the same type anyhow
    – user463035818
    Nov 22 at 12:47








  • 3




    std::map<K, V>::size_type is most likely completely independent of both K and V. If you really care, you can static_assert(std::is_same_v<Map::size_type, Map::mapped_type>, "Unexpected size_type")
    – Caleth
    Nov 22 at 12:59


















  • minor nitpick: I think you have the last sentence the wrong way around. To know what is size_type you first need to tell what is the value_type not the other way around. Once you know the type of the map, getting its size_type is trivial
    – user463035818
    Nov 22 at 12:44












  • @user463035818 The problem seems to be that the size_type is a part of value_type for the OP.
    – Some programmer dude
    Nov 22 at 12:46






  • 5




    You have a circular dependency. Why can't you use size_t (which is what size_type normally will be anyway)?
    – Some programmer dude
    Nov 22 at 12:47










  • can you explain why you want that? afaik map::size_type is just a typedef aliasing always the same type anyhow
    – user463035818
    Nov 22 at 12:47








  • 3




    std::map<K, V>::size_type is most likely completely independent of both K and V. If you really care, you can static_assert(std::is_same_v<Map::size_type, Map::mapped_type>, "Unexpected size_type")
    – Caleth
    Nov 22 at 12:59
















minor nitpick: I think you have the last sentence the wrong way around. To know what is size_type you first need to tell what is the value_type not the other way around. Once you know the type of the map, getting its size_type is trivial
– user463035818
Nov 22 at 12:44






minor nitpick: I think you have the last sentence the wrong way around. To know what is size_type you first need to tell what is the value_type not the other way around. Once you know the type of the map, getting its size_type is trivial
– user463035818
Nov 22 at 12:44














@user463035818 The problem seems to be that the size_type is a part of value_type for the OP.
– Some programmer dude
Nov 22 at 12:46




@user463035818 The problem seems to be that the size_type is a part of value_type for the OP.
– Some programmer dude
Nov 22 at 12:46




5




5




You have a circular dependency. Why can't you use size_t (which is what size_type normally will be anyway)?
– Some programmer dude
Nov 22 at 12:47




You have a circular dependency. Why can't you use size_t (which is what size_type normally will be anyway)?
– Some programmer dude
Nov 22 at 12:47












can you explain why you want that? afaik map::size_type is just a typedef aliasing always the same type anyhow
– user463035818
Nov 22 at 12:47






can you explain why you want that? afaik map::size_type is just a typedef aliasing always the same type anyhow
– user463035818
Nov 22 at 12:47






3




3




std::map<K, V>::size_type is most likely completely independent of both K and V. If you really care, you can static_assert(std::is_same_v<Map::size_type, Map::mapped_type>, "Unexpected size_type")
– Caleth
Nov 22 at 12:59




std::map<K, V>::size_type is most likely completely independent of both K and V. If you really care, you can static_assert(std::is_same_v<Map::size_type, Map::mapped_type>, "Unexpected size_type")
– Caleth
Nov 22 at 12:59












7 Answers
7






active

oldest

votes

















up vote
1
down vote



accepted










What you are looking for is, generally speaking, impossible.



It's conceivable (though far-fetched) that std::map<int, long>::size_type is int and std::map<int, int>::size_type is long (and similarly for other integer types), in which case there is no possible way to satisfy std::map<int, T>::size_type being T.



Conversely, it could be that std::map<int, T>::size_type is defined as T for all T, in which case there is no unique T satisfying your "requirement".



As mentioned by several answers (and your own reference link), in practice it's unlikely to be anything else than size_t.






share|improve this answer





















  • This is the answer I was looking for, it just isn't the one I was hoping for. The specific examples you provide illustrate the problem perfectly.
    – vallismortis
    Nov 22 at 13:27


















up vote
5
down vote













size_t should be good enough for such case.



But if you insist, you can do like this:



#include <type_traits>
#include <map>

template <class Key, class Value = size_t, size_t depth = 0, class = void>
struct GetSizeType {
using type = typename GetSizeType<Key, typename std::map<Key, Value>::size_type, depth + 1>::type;
};

template <class Key, class Value, size_t depth>
struct GetSizeType<Key, Value, depth, std::enable_if_t<std::is_same_v<Value, typename std::map<Key, Value>::size_type>>> {
using type = typename std::map<Key, Value>::size_type;
};

template <class Key, class Value>
struct GetSizeType<Key, Value, 100, void> {};

int main() {
using X = GetSizeType<int>::type;

return 0;
}


It will run recursively on GetSizeType, the recursive call will stop upon




  • reaching the recursive call depth limitation (there will be no member type in this case), or

  • finding a specialization of std::map of which the mapped_type and size_type is identical (the member type aliases size_type).






share|improve this answer























  • Technical thug approach of the first order - brilliant!
    – Toby Speight
    Nov 22 at 13:02






  • 1




    i dont completely agree with " You will run out of memory before running out of size_t.". Something like my_map.size() * 2; is a sensible operation that can overflow long before you run out of memory
    – user463035818
    Nov 22 at 13:06






  • 2




    @user463035818 Thanks, I have removed that part. I wrongly simplified the usage.
    – felix
    Nov 22 at 13:07






  • 1




    @user463035818 - in the context set by the question, there's no arithmetic involved - the result of size() is simply being used as an opaque identifier.
    – Toby Speight
    Nov 22 at 13:08








  • 1




    @vallismortis What do you mean by "more extreme cases"? If you use std::size_t, there is no way it won't work where another type would, because that should be able to address even objects of size 1.
    – Acorn
    Nov 22 at 13:22




















up vote
4
down vote













Disclaimer: this solution is pretty dumb. We're just going to solve the equation by repeatedly (typically once) trying to instantiate std::map until we find one that has the requested key and its own size_type as value.



template <class T>
struct identity {
using type = T;
};

template <class K, class V = char>
struct auto_map {
using map_type = std::map<K, V>;
using type = typename std::conditional_t<
std::is_same_v<
typename map_type::mapped_type,
typename map_type::size_type
>,
identity<map_type>,
auto_map<K, typename map_type::size_type>
>::type;
};

template <class K>
using auto_map_t = typename auto_map<K>::type;


If the metafunction can't find such a map, it will either error out because type ends up defined to itself, or break the recursion limit.






share|improve this answer





















  • Both recursive solutions shown are cool, but I am unsure why one would use this. It still does not guarantee you will find one that works in a broken environment, as you say, so it is cleaner and faster to compile to simply pick one and assert the condition we actually need, no?
    – Acorn
    Nov 22 at 13:24










  • @Acorn yes. I just needed something to do while I'm doing a full rebuild ;)
    – Quentin
    Nov 22 at 13:27






  • 1




    @Quentin same here, cheers.
    – felix
    Nov 22 at 13:31


















up vote
2
down vote













Use std::size_t. The unsigned integer std::map::size_type won't be bigger than std::size_t and will, in practice, be the same type.



If you want to be sure, assert it:



static_assert(std::is_same_v<
std::size_t,
std::map<std::string, std::size_t>::size_type
>);





share|improve this answer




























    up vote
    2
    down vote













    All C++ implementations in the wild I have used use the same size type for all maps.



    So;



    using map_size_type = std::map<int, int>::size_type;
    using my_map = std::map<std::string, map_size_type>;
    static_assert(std::is_same<map_size_type, my_map::size_type);


    this just forces a compilation error if the (reasonable) assumption fails.






    share|improve this answer























    • I'm linking another question here because your answer seems relevant to it as well.
      – vallismortis
      Nov 22 at 13:44


















    up vote
    1
    down vote













    The only way to break the circular dependency is to use a specific type. I recommend that you simply make map_index be a std::size_t - C++ strongly implies that a std::size_t will be assignable to the map::size_type.






    share|improve this answer




























      up vote
      1
      down vote













      But are you sure that the size_type of a std::map depends from the key/value types?



      If so, I don't see a way to get it.



      But the size_type shouldn't depends from key/value types and usually is std::size_t.



      I suggest



      using Index0 = typename std::map<std::string, std::size_t>::size_type;

      using mapIndex = typename std::map<std::string, Index0>::size_type;


      You can check you've gotten the right type with



      static_assert( std::is_same_v<Index0, mapIndex>, "no right type");





      share|improve this answer



















      • 1




        The compiler has no way to know that the map's size type is always the same - as far as it's concerned, there could legitimately be a specialization that's different.
        – Toby Speight
        Nov 22 at 12:53










      • @TobySpeight Yes, that is exactly what prompted me to ask this question. It is that "usually the same as size_t" clause from the C++ map documentation that really got me thinking about this.
        – vallismortis
        Nov 22 at 12:59








      • 1




        @vallismortis - from theoretical point of view, you're right (as far I know). I don't see a way to break the circular dependency but adding static_assert( std::is_same_v<Index0, mapIndex>, "no right type"); you can check that the selected type is the right one.
        – max66
        Nov 22 at 13:04






      • 1




        @TobySpeight - you're right (as far I know); but through a static_assert() (see my modified answer) we can check if we have gotten the right type.
        – max66
        Nov 22 at 13:06











      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53431281%2fstdmapsize-type-for-a-stdmap-whose-value-type-is-its-own-size-type%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      7 Answers
      7






      active

      oldest

      votes








      7 Answers
      7






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      1
      down vote



      accepted










      What you are looking for is, generally speaking, impossible.



      It's conceivable (though far-fetched) that std::map<int, long>::size_type is int and std::map<int, int>::size_type is long (and similarly for other integer types), in which case there is no possible way to satisfy std::map<int, T>::size_type being T.



      Conversely, it could be that std::map<int, T>::size_type is defined as T for all T, in which case there is no unique T satisfying your "requirement".



      As mentioned by several answers (and your own reference link), in practice it's unlikely to be anything else than size_t.






      share|improve this answer





















      • This is the answer I was looking for, it just isn't the one I was hoping for. The specific examples you provide illustrate the problem perfectly.
        – vallismortis
        Nov 22 at 13:27















      up vote
      1
      down vote



      accepted










      What you are looking for is, generally speaking, impossible.



      It's conceivable (though far-fetched) that std::map<int, long>::size_type is int and std::map<int, int>::size_type is long (and similarly for other integer types), in which case there is no possible way to satisfy std::map<int, T>::size_type being T.



      Conversely, it could be that std::map<int, T>::size_type is defined as T for all T, in which case there is no unique T satisfying your "requirement".



      As mentioned by several answers (and your own reference link), in practice it's unlikely to be anything else than size_t.






      share|improve this answer





















      • This is the answer I was looking for, it just isn't the one I was hoping for. The specific examples you provide illustrate the problem perfectly.
        – vallismortis
        Nov 22 at 13:27













      up vote
      1
      down vote



      accepted







      up vote
      1
      down vote



      accepted






      What you are looking for is, generally speaking, impossible.



      It's conceivable (though far-fetched) that std::map<int, long>::size_type is int and std::map<int, int>::size_type is long (and similarly for other integer types), in which case there is no possible way to satisfy std::map<int, T>::size_type being T.



      Conversely, it could be that std::map<int, T>::size_type is defined as T for all T, in which case there is no unique T satisfying your "requirement".



      As mentioned by several answers (and your own reference link), in practice it's unlikely to be anything else than size_t.






      share|improve this answer












      What you are looking for is, generally speaking, impossible.



      It's conceivable (though far-fetched) that std::map<int, long>::size_type is int and std::map<int, int>::size_type is long (and similarly for other integer types), in which case there is no possible way to satisfy std::map<int, T>::size_type being T.



      Conversely, it could be that std::map<int, T>::size_type is defined as T for all T, in which case there is no unique T satisfying your "requirement".



      As mentioned by several answers (and your own reference link), in practice it's unlikely to be anything else than size_t.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Nov 22 at 13:24









      Max Langhof

      8,1181335




      8,1181335












      • This is the answer I was looking for, it just isn't the one I was hoping for. The specific examples you provide illustrate the problem perfectly.
        – vallismortis
        Nov 22 at 13:27


















      • This is the answer I was looking for, it just isn't the one I was hoping for. The specific examples you provide illustrate the problem perfectly.
        – vallismortis
        Nov 22 at 13:27
















      This is the answer I was looking for, it just isn't the one I was hoping for. The specific examples you provide illustrate the problem perfectly.
      – vallismortis
      Nov 22 at 13:27




      This is the answer I was looking for, it just isn't the one I was hoping for. The specific examples you provide illustrate the problem perfectly.
      – vallismortis
      Nov 22 at 13:27












      up vote
      5
      down vote













      size_t should be good enough for such case.



      But if you insist, you can do like this:



      #include <type_traits>
      #include <map>

      template <class Key, class Value = size_t, size_t depth = 0, class = void>
      struct GetSizeType {
      using type = typename GetSizeType<Key, typename std::map<Key, Value>::size_type, depth + 1>::type;
      };

      template <class Key, class Value, size_t depth>
      struct GetSizeType<Key, Value, depth, std::enable_if_t<std::is_same_v<Value, typename std::map<Key, Value>::size_type>>> {
      using type = typename std::map<Key, Value>::size_type;
      };

      template <class Key, class Value>
      struct GetSizeType<Key, Value, 100, void> {};

      int main() {
      using X = GetSizeType<int>::type;

      return 0;
      }


      It will run recursively on GetSizeType, the recursive call will stop upon




      • reaching the recursive call depth limitation (there will be no member type in this case), or

      • finding a specialization of std::map of which the mapped_type and size_type is identical (the member type aliases size_type).






      share|improve this answer























      • Technical thug approach of the first order - brilliant!
        – Toby Speight
        Nov 22 at 13:02






      • 1




        i dont completely agree with " You will run out of memory before running out of size_t.". Something like my_map.size() * 2; is a sensible operation that can overflow long before you run out of memory
        – user463035818
        Nov 22 at 13:06






      • 2




        @user463035818 Thanks, I have removed that part. I wrongly simplified the usage.
        – felix
        Nov 22 at 13:07






      • 1




        @user463035818 - in the context set by the question, there's no arithmetic involved - the result of size() is simply being used as an opaque identifier.
        – Toby Speight
        Nov 22 at 13:08








      • 1




        @vallismortis What do you mean by "more extreme cases"? If you use std::size_t, there is no way it won't work where another type would, because that should be able to address even objects of size 1.
        – Acorn
        Nov 22 at 13:22

















      up vote
      5
      down vote













      size_t should be good enough for such case.



      But if you insist, you can do like this:



      #include <type_traits>
      #include <map>

      template <class Key, class Value = size_t, size_t depth = 0, class = void>
      struct GetSizeType {
      using type = typename GetSizeType<Key, typename std::map<Key, Value>::size_type, depth + 1>::type;
      };

      template <class Key, class Value, size_t depth>
      struct GetSizeType<Key, Value, depth, std::enable_if_t<std::is_same_v<Value, typename std::map<Key, Value>::size_type>>> {
      using type = typename std::map<Key, Value>::size_type;
      };

      template <class Key, class Value>
      struct GetSizeType<Key, Value, 100, void> {};

      int main() {
      using X = GetSizeType<int>::type;

      return 0;
      }


      It will run recursively on GetSizeType, the recursive call will stop upon




      • reaching the recursive call depth limitation (there will be no member type in this case), or

      • finding a specialization of std::map of which the mapped_type and size_type is identical (the member type aliases size_type).






      share|improve this answer























      • Technical thug approach of the first order - brilliant!
        – Toby Speight
        Nov 22 at 13:02






      • 1




        i dont completely agree with " You will run out of memory before running out of size_t.". Something like my_map.size() * 2; is a sensible operation that can overflow long before you run out of memory
        – user463035818
        Nov 22 at 13:06






      • 2




        @user463035818 Thanks, I have removed that part. I wrongly simplified the usage.
        – felix
        Nov 22 at 13:07






      • 1




        @user463035818 - in the context set by the question, there's no arithmetic involved - the result of size() is simply being used as an opaque identifier.
        – Toby Speight
        Nov 22 at 13:08








      • 1




        @vallismortis What do you mean by "more extreme cases"? If you use std::size_t, there is no way it won't work where another type would, because that should be able to address even objects of size 1.
        – Acorn
        Nov 22 at 13:22















      up vote
      5
      down vote










      up vote
      5
      down vote









      size_t should be good enough for such case.



      But if you insist, you can do like this:



      #include <type_traits>
      #include <map>

      template <class Key, class Value = size_t, size_t depth = 0, class = void>
      struct GetSizeType {
      using type = typename GetSizeType<Key, typename std::map<Key, Value>::size_type, depth + 1>::type;
      };

      template <class Key, class Value, size_t depth>
      struct GetSizeType<Key, Value, depth, std::enable_if_t<std::is_same_v<Value, typename std::map<Key, Value>::size_type>>> {
      using type = typename std::map<Key, Value>::size_type;
      };

      template <class Key, class Value>
      struct GetSizeType<Key, Value, 100, void> {};

      int main() {
      using X = GetSizeType<int>::type;

      return 0;
      }


      It will run recursively on GetSizeType, the recursive call will stop upon




      • reaching the recursive call depth limitation (there will be no member type in this case), or

      • finding a specialization of std::map of which the mapped_type and size_type is identical (the member type aliases size_type).






      share|improve this answer














      size_t should be good enough for such case.



      But if you insist, you can do like this:



      #include <type_traits>
      #include <map>

      template <class Key, class Value = size_t, size_t depth = 0, class = void>
      struct GetSizeType {
      using type = typename GetSizeType<Key, typename std::map<Key, Value>::size_type, depth + 1>::type;
      };

      template <class Key, class Value, size_t depth>
      struct GetSizeType<Key, Value, depth, std::enable_if_t<std::is_same_v<Value, typename std::map<Key, Value>::size_type>>> {
      using type = typename std::map<Key, Value>::size_type;
      };

      template <class Key, class Value>
      struct GetSizeType<Key, Value, 100, void> {};

      int main() {
      using X = GetSizeType<int>::type;

      return 0;
      }


      It will run recursively on GetSizeType, the recursive call will stop upon




      • reaching the recursive call depth limitation (there will be no member type in this case), or

      • finding a specialization of std::map of which the mapped_type and size_type is identical (the member type aliases size_type).







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 22 at 15:28

























      answered Nov 22 at 13:01









      felix

      1,428314




      1,428314












      • Technical thug approach of the first order - brilliant!
        – Toby Speight
        Nov 22 at 13:02






      • 1




        i dont completely agree with " You will run out of memory before running out of size_t.". Something like my_map.size() * 2; is a sensible operation that can overflow long before you run out of memory
        – user463035818
        Nov 22 at 13:06






      • 2




        @user463035818 Thanks, I have removed that part. I wrongly simplified the usage.
        – felix
        Nov 22 at 13:07






      • 1




        @user463035818 - in the context set by the question, there's no arithmetic involved - the result of size() is simply being used as an opaque identifier.
        – Toby Speight
        Nov 22 at 13:08








      • 1




        @vallismortis What do you mean by "more extreme cases"? If you use std::size_t, there is no way it won't work where another type would, because that should be able to address even objects of size 1.
        – Acorn
        Nov 22 at 13:22




















      • Technical thug approach of the first order - brilliant!
        – Toby Speight
        Nov 22 at 13:02






      • 1




        i dont completely agree with " You will run out of memory before running out of size_t.". Something like my_map.size() * 2; is a sensible operation that can overflow long before you run out of memory
        – user463035818
        Nov 22 at 13:06






      • 2




        @user463035818 Thanks, I have removed that part. I wrongly simplified the usage.
        – felix
        Nov 22 at 13:07






      • 1




        @user463035818 - in the context set by the question, there's no arithmetic involved - the result of size() is simply being used as an opaque identifier.
        – Toby Speight
        Nov 22 at 13:08








      • 1




        @vallismortis What do you mean by "more extreme cases"? If you use std::size_t, there is no way it won't work where another type would, because that should be able to address even objects of size 1.
        – Acorn
        Nov 22 at 13:22


















      Technical thug approach of the first order - brilliant!
      – Toby Speight
      Nov 22 at 13:02




      Technical thug approach of the first order - brilliant!
      – Toby Speight
      Nov 22 at 13:02




      1




      1




      i dont completely agree with " You will run out of memory before running out of size_t.". Something like my_map.size() * 2; is a sensible operation that can overflow long before you run out of memory
      – user463035818
      Nov 22 at 13:06




      i dont completely agree with " You will run out of memory before running out of size_t.". Something like my_map.size() * 2; is a sensible operation that can overflow long before you run out of memory
      – user463035818
      Nov 22 at 13:06




      2




      2




      @user463035818 Thanks, I have removed that part. I wrongly simplified the usage.
      – felix
      Nov 22 at 13:07




      @user463035818 Thanks, I have removed that part. I wrongly simplified the usage.
      – felix
      Nov 22 at 13:07




      1




      1




      @user463035818 - in the context set by the question, there's no arithmetic involved - the result of size() is simply being used as an opaque identifier.
      – Toby Speight
      Nov 22 at 13:08






      @user463035818 - in the context set by the question, there's no arithmetic involved - the result of size() is simply being used as an opaque identifier.
      – Toby Speight
      Nov 22 at 13:08






      1




      1




      @vallismortis What do you mean by "more extreme cases"? If you use std::size_t, there is no way it won't work where another type would, because that should be able to address even objects of size 1.
      – Acorn
      Nov 22 at 13:22






      @vallismortis What do you mean by "more extreme cases"? If you use std::size_t, there is no way it won't work where another type would, because that should be able to address even objects of size 1.
      – Acorn
      Nov 22 at 13:22












      up vote
      4
      down vote













      Disclaimer: this solution is pretty dumb. We're just going to solve the equation by repeatedly (typically once) trying to instantiate std::map until we find one that has the requested key and its own size_type as value.



      template <class T>
      struct identity {
      using type = T;
      };

      template <class K, class V = char>
      struct auto_map {
      using map_type = std::map<K, V>;
      using type = typename std::conditional_t<
      std::is_same_v<
      typename map_type::mapped_type,
      typename map_type::size_type
      >,
      identity<map_type>,
      auto_map<K, typename map_type::size_type>
      >::type;
      };

      template <class K>
      using auto_map_t = typename auto_map<K>::type;


      If the metafunction can't find such a map, it will either error out because type ends up defined to itself, or break the recursion limit.






      share|improve this answer





















      • Both recursive solutions shown are cool, but I am unsure why one would use this. It still does not guarantee you will find one that works in a broken environment, as you say, so it is cleaner and faster to compile to simply pick one and assert the condition we actually need, no?
        – Acorn
        Nov 22 at 13:24










      • @Acorn yes. I just needed something to do while I'm doing a full rebuild ;)
        – Quentin
        Nov 22 at 13:27






      • 1




        @Quentin same here, cheers.
        – felix
        Nov 22 at 13:31















      up vote
      4
      down vote













      Disclaimer: this solution is pretty dumb. We're just going to solve the equation by repeatedly (typically once) trying to instantiate std::map until we find one that has the requested key and its own size_type as value.



      template <class T>
      struct identity {
      using type = T;
      };

      template <class K, class V = char>
      struct auto_map {
      using map_type = std::map<K, V>;
      using type = typename std::conditional_t<
      std::is_same_v<
      typename map_type::mapped_type,
      typename map_type::size_type
      >,
      identity<map_type>,
      auto_map<K, typename map_type::size_type>
      >::type;
      };

      template <class K>
      using auto_map_t = typename auto_map<K>::type;


      If the metafunction can't find such a map, it will either error out because type ends up defined to itself, or break the recursion limit.






      share|improve this answer





















      • Both recursive solutions shown are cool, but I am unsure why one would use this. It still does not guarantee you will find one that works in a broken environment, as you say, so it is cleaner and faster to compile to simply pick one and assert the condition we actually need, no?
        – Acorn
        Nov 22 at 13:24










      • @Acorn yes. I just needed something to do while I'm doing a full rebuild ;)
        – Quentin
        Nov 22 at 13:27






      • 1




        @Quentin same here, cheers.
        – felix
        Nov 22 at 13:31













      up vote
      4
      down vote










      up vote
      4
      down vote









      Disclaimer: this solution is pretty dumb. We're just going to solve the equation by repeatedly (typically once) trying to instantiate std::map until we find one that has the requested key and its own size_type as value.



      template <class T>
      struct identity {
      using type = T;
      };

      template <class K, class V = char>
      struct auto_map {
      using map_type = std::map<K, V>;
      using type = typename std::conditional_t<
      std::is_same_v<
      typename map_type::mapped_type,
      typename map_type::size_type
      >,
      identity<map_type>,
      auto_map<K, typename map_type::size_type>
      >::type;
      };

      template <class K>
      using auto_map_t = typename auto_map<K>::type;


      If the metafunction can't find such a map, it will either error out because type ends up defined to itself, or break the recursion limit.






      share|improve this answer












      Disclaimer: this solution is pretty dumb. We're just going to solve the equation by repeatedly (typically once) trying to instantiate std::map until we find one that has the requested key and its own size_type as value.



      template <class T>
      struct identity {
      using type = T;
      };

      template <class K, class V = char>
      struct auto_map {
      using map_type = std::map<K, V>;
      using type = typename std::conditional_t<
      std::is_same_v<
      typename map_type::mapped_type,
      typename map_type::size_type
      >,
      identity<map_type>,
      auto_map<K, typename map_type::size_type>
      >::type;
      };

      template <class K>
      using auto_map_t = typename auto_map<K>::type;


      If the metafunction can't find such a map, it will either error out because type ends up defined to itself, or break the recursion limit.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Nov 22 at 13:02









      Quentin

      44.1k583139




      44.1k583139












      • Both recursive solutions shown are cool, but I am unsure why one would use this. It still does not guarantee you will find one that works in a broken environment, as you say, so it is cleaner and faster to compile to simply pick one and assert the condition we actually need, no?
        – Acorn
        Nov 22 at 13:24










      • @Acorn yes. I just needed something to do while I'm doing a full rebuild ;)
        – Quentin
        Nov 22 at 13:27






      • 1




        @Quentin same here, cheers.
        – felix
        Nov 22 at 13:31


















      • Both recursive solutions shown are cool, but I am unsure why one would use this. It still does not guarantee you will find one that works in a broken environment, as you say, so it is cleaner and faster to compile to simply pick one and assert the condition we actually need, no?
        – Acorn
        Nov 22 at 13:24










      • @Acorn yes. I just needed something to do while I'm doing a full rebuild ;)
        – Quentin
        Nov 22 at 13:27






      • 1




        @Quentin same here, cheers.
        – felix
        Nov 22 at 13:31
















      Both recursive solutions shown are cool, but I am unsure why one would use this. It still does not guarantee you will find one that works in a broken environment, as you say, so it is cleaner and faster to compile to simply pick one and assert the condition we actually need, no?
      – Acorn
      Nov 22 at 13:24




      Both recursive solutions shown are cool, but I am unsure why one would use this. It still does not guarantee you will find one that works in a broken environment, as you say, so it is cleaner and faster to compile to simply pick one and assert the condition we actually need, no?
      – Acorn
      Nov 22 at 13:24












      @Acorn yes. I just needed something to do while I'm doing a full rebuild ;)
      – Quentin
      Nov 22 at 13:27




      @Acorn yes. I just needed something to do while I'm doing a full rebuild ;)
      – Quentin
      Nov 22 at 13:27




      1




      1




      @Quentin same here, cheers.
      – felix
      Nov 22 at 13:31




      @Quentin same here, cheers.
      – felix
      Nov 22 at 13:31










      up vote
      2
      down vote













      Use std::size_t. The unsigned integer std::map::size_type won't be bigger than std::size_t and will, in practice, be the same type.



      If you want to be sure, assert it:



      static_assert(std::is_same_v<
      std::size_t,
      std::map<std::string, std::size_t>::size_type
      >);





      share|improve this answer

























        up vote
        2
        down vote













        Use std::size_t. The unsigned integer std::map::size_type won't be bigger than std::size_t and will, in practice, be the same type.



        If you want to be sure, assert it:



        static_assert(std::is_same_v<
        std::size_t,
        std::map<std::string, std::size_t>::size_type
        >);





        share|improve this answer























          up vote
          2
          down vote










          up vote
          2
          down vote









          Use std::size_t. The unsigned integer std::map::size_type won't be bigger than std::size_t and will, in practice, be the same type.



          If you want to be sure, assert it:



          static_assert(std::is_same_v<
          std::size_t,
          std::map<std::string, std::size_t>::size_type
          >);





          share|improve this answer












          Use std::size_t. The unsigned integer std::map::size_type won't be bigger than std::size_t and will, in practice, be the same type.



          If you want to be sure, assert it:



          static_assert(std::is_same_v<
          std::size_t,
          std::map<std::string, std::size_t>::size_type
          >);






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 22 at 13:04









          Acorn

          4,83711135




          4,83711135






















              up vote
              2
              down vote













              All C++ implementations in the wild I have used use the same size type for all maps.



              So;



              using map_size_type = std::map<int, int>::size_type;
              using my_map = std::map<std::string, map_size_type>;
              static_assert(std::is_same<map_size_type, my_map::size_type);


              this just forces a compilation error if the (reasonable) assumption fails.






              share|improve this answer























              • I'm linking another question here because your answer seems relevant to it as well.
                – vallismortis
                Nov 22 at 13:44















              up vote
              2
              down vote













              All C++ implementations in the wild I have used use the same size type for all maps.



              So;



              using map_size_type = std::map<int, int>::size_type;
              using my_map = std::map<std::string, map_size_type>;
              static_assert(std::is_same<map_size_type, my_map::size_type);


              this just forces a compilation error if the (reasonable) assumption fails.






              share|improve this answer























              • I'm linking another question here because your answer seems relevant to it as well.
                – vallismortis
                Nov 22 at 13:44













              up vote
              2
              down vote










              up vote
              2
              down vote









              All C++ implementations in the wild I have used use the same size type for all maps.



              So;



              using map_size_type = std::map<int, int>::size_type;
              using my_map = std::map<std::string, map_size_type>;
              static_assert(std::is_same<map_size_type, my_map::size_type);


              this just forces a compilation error if the (reasonable) assumption fails.






              share|improve this answer














              All C++ implementations in the wild I have used use the same size type for all maps.



              So;



              using map_size_type = std::map<int, int>::size_type;
              using my_map = std::map<std::string, map_size_type>;
              static_assert(std::is_same<map_size_type, my_map::size_type);


              this just forces a compilation error if the (reasonable) assumption fails.







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Nov 22 at 13:45









              vallismortis

              3,367103962




              3,367103962










              answered Nov 22 at 13:33









              Yakk - Adam Nevraumont

              180k19188367




              180k19188367












              • I'm linking another question here because your answer seems relevant to it as well.
                – vallismortis
                Nov 22 at 13:44


















              • I'm linking another question here because your answer seems relevant to it as well.
                – vallismortis
                Nov 22 at 13:44
















              I'm linking another question here because your answer seems relevant to it as well.
              – vallismortis
              Nov 22 at 13:44




              I'm linking another question here because your answer seems relevant to it as well.
              – vallismortis
              Nov 22 at 13:44










              up vote
              1
              down vote













              The only way to break the circular dependency is to use a specific type. I recommend that you simply make map_index be a std::size_t - C++ strongly implies that a std::size_t will be assignable to the map::size_type.






              share|improve this answer

























                up vote
                1
                down vote













                The only way to break the circular dependency is to use a specific type. I recommend that you simply make map_index be a std::size_t - C++ strongly implies that a std::size_t will be assignable to the map::size_type.






                share|improve this answer























                  up vote
                  1
                  down vote










                  up vote
                  1
                  down vote









                  The only way to break the circular dependency is to use a specific type. I recommend that you simply make map_index be a std::size_t - C++ strongly implies that a std::size_t will be assignable to the map::size_type.






                  share|improve this answer












                  The only way to break the circular dependency is to use a specific type. I recommend that you simply make map_index be a std::size_t - C++ strongly implies that a std::size_t will be assignable to the map::size_type.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 22 at 13:00









                  Toby Speight

                  16.1k133965




                  16.1k133965






















                      up vote
                      1
                      down vote













                      But are you sure that the size_type of a std::map depends from the key/value types?



                      If so, I don't see a way to get it.



                      But the size_type shouldn't depends from key/value types and usually is std::size_t.



                      I suggest



                      using Index0 = typename std::map<std::string, std::size_t>::size_type;

                      using mapIndex = typename std::map<std::string, Index0>::size_type;


                      You can check you've gotten the right type with



                      static_assert( std::is_same_v<Index0, mapIndex>, "no right type");





                      share|improve this answer



















                      • 1




                        The compiler has no way to know that the map's size type is always the same - as far as it's concerned, there could legitimately be a specialization that's different.
                        – Toby Speight
                        Nov 22 at 12:53










                      • @TobySpeight Yes, that is exactly what prompted me to ask this question. It is that "usually the same as size_t" clause from the C++ map documentation that really got me thinking about this.
                        – vallismortis
                        Nov 22 at 12:59








                      • 1




                        @vallismortis - from theoretical point of view, you're right (as far I know). I don't see a way to break the circular dependency but adding static_assert( std::is_same_v<Index0, mapIndex>, "no right type"); you can check that the selected type is the right one.
                        – max66
                        Nov 22 at 13:04






                      • 1




                        @TobySpeight - you're right (as far I know); but through a static_assert() (see my modified answer) we can check if we have gotten the right type.
                        – max66
                        Nov 22 at 13:06















                      up vote
                      1
                      down vote













                      But are you sure that the size_type of a std::map depends from the key/value types?



                      If so, I don't see a way to get it.



                      But the size_type shouldn't depends from key/value types and usually is std::size_t.



                      I suggest



                      using Index0 = typename std::map<std::string, std::size_t>::size_type;

                      using mapIndex = typename std::map<std::string, Index0>::size_type;


                      You can check you've gotten the right type with



                      static_assert( std::is_same_v<Index0, mapIndex>, "no right type");





                      share|improve this answer



















                      • 1




                        The compiler has no way to know that the map's size type is always the same - as far as it's concerned, there could legitimately be a specialization that's different.
                        – Toby Speight
                        Nov 22 at 12:53










                      • @TobySpeight Yes, that is exactly what prompted me to ask this question. It is that "usually the same as size_t" clause from the C++ map documentation that really got me thinking about this.
                        – vallismortis
                        Nov 22 at 12:59








                      • 1




                        @vallismortis - from theoretical point of view, you're right (as far I know). I don't see a way to break the circular dependency but adding static_assert( std::is_same_v<Index0, mapIndex>, "no right type"); you can check that the selected type is the right one.
                        – max66
                        Nov 22 at 13:04






                      • 1




                        @TobySpeight - you're right (as far I know); but through a static_assert() (see my modified answer) we can check if we have gotten the right type.
                        – max66
                        Nov 22 at 13:06













                      up vote
                      1
                      down vote










                      up vote
                      1
                      down vote









                      But are you sure that the size_type of a std::map depends from the key/value types?



                      If so, I don't see a way to get it.



                      But the size_type shouldn't depends from key/value types and usually is std::size_t.



                      I suggest



                      using Index0 = typename std::map<std::string, std::size_t>::size_type;

                      using mapIndex = typename std::map<std::string, Index0>::size_type;


                      You can check you've gotten the right type with



                      static_assert( std::is_same_v<Index0, mapIndex>, "no right type");





                      share|improve this answer














                      But are you sure that the size_type of a std::map depends from the key/value types?



                      If so, I don't see a way to get it.



                      But the size_type shouldn't depends from key/value types and usually is std::size_t.



                      I suggest



                      using Index0 = typename std::map<std::string, std::size_t>::size_type;

                      using mapIndex = typename std::map<std::string, Index0>::size_type;


                      You can check you've gotten the right type with



                      static_assert( std::is_same_v<Index0, mapIndex>, "no right type");






                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Nov 22 at 13:10

























                      answered Nov 22 at 12:49









                      max66

                      33.7k63762




                      33.7k63762








                      • 1




                        The compiler has no way to know that the map's size type is always the same - as far as it's concerned, there could legitimately be a specialization that's different.
                        – Toby Speight
                        Nov 22 at 12:53










                      • @TobySpeight Yes, that is exactly what prompted me to ask this question. It is that "usually the same as size_t" clause from the C++ map documentation that really got me thinking about this.
                        – vallismortis
                        Nov 22 at 12:59








                      • 1




                        @vallismortis - from theoretical point of view, you're right (as far I know). I don't see a way to break the circular dependency but adding static_assert( std::is_same_v<Index0, mapIndex>, "no right type"); you can check that the selected type is the right one.
                        – max66
                        Nov 22 at 13:04






                      • 1




                        @TobySpeight - you're right (as far I know); but through a static_assert() (see my modified answer) we can check if we have gotten the right type.
                        – max66
                        Nov 22 at 13:06














                      • 1




                        The compiler has no way to know that the map's size type is always the same - as far as it's concerned, there could legitimately be a specialization that's different.
                        – Toby Speight
                        Nov 22 at 12:53










                      • @TobySpeight Yes, that is exactly what prompted me to ask this question. It is that "usually the same as size_t" clause from the C++ map documentation that really got me thinking about this.
                        – vallismortis
                        Nov 22 at 12:59








                      • 1




                        @vallismortis - from theoretical point of view, you're right (as far I know). I don't see a way to break the circular dependency but adding static_assert( std::is_same_v<Index0, mapIndex>, "no right type"); you can check that the selected type is the right one.
                        – max66
                        Nov 22 at 13:04






                      • 1




                        @TobySpeight - you're right (as far I know); but through a static_assert() (see my modified answer) we can check if we have gotten the right type.
                        – max66
                        Nov 22 at 13:06








                      1




                      1




                      The compiler has no way to know that the map's size type is always the same - as far as it's concerned, there could legitimately be a specialization that's different.
                      – Toby Speight
                      Nov 22 at 12:53




                      The compiler has no way to know that the map's size type is always the same - as far as it's concerned, there could legitimately be a specialization that's different.
                      – Toby Speight
                      Nov 22 at 12:53












                      @TobySpeight Yes, that is exactly what prompted me to ask this question. It is that "usually the same as size_t" clause from the C++ map documentation that really got me thinking about this.
                      – vallismortis
                      Nov 22 at 12:59






                      @TobySpeight Yes, that is exactly what prompted me to ask this question. It is that "usually the same as size_t" clause from the C++ map documentation that really got me thinking about this.
                      – vallismortis
                      Nov 22 at 12:59






                      1




                      1




                      @vallismortis - from theoretical point of view, you're right (as far I know). I don't see a way to break the circular dependency but adding static_assert( std::is_same_v<Index0, mapIndex>, "no right type"); you can check that the selected type is the right one.
                      – max66
                      Nov 22 at 13:04




                      @vallismortis - from theoretical point of view, you're right (as far I know). I don't see a way to break the circular dependency but adding static_assert( std::is_same_v<Index0, mapIndex>, "no right type"); you can check that the selected type is the right one.
                      – max66
                      Nov 22 at 13:04




                      1




                      1




                      @TobySpeight - you're right (as far I know); but through a static_assert() (see my modified answer) we can check if we have gotten the right type.
                      – max66
                      Nov 22 at 13:06




                      @TobySpeight - you're right (as far I know); but through a static_assert() (see my modified answer) we can check if we have gotten the right type.
                      – max66
                      Nov 22 at 13:06


















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53431281%2fstdmapsize-type-for-a-stdmap-whose-value-type-is-its-own-size-type%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      What visual should I use to simply compare current year value vs last year in Power BI desktop

                      Alexandru Averescu

                      Trompette piccolo