3

I am writing a function where I am copying Keys of map, set, unordered_map, unordered_set to a vector, now I want to add a compile time assert to get clear error if some try to pass a vector, list in that function.

template <typename container>
auto CopyKeyToVector(conatiner c)
{
  //static assert to check c is map, unordered map only?


}

Any idea how we can do that- as map, unordered_map itself templatized container

gaurav bharadwaj
  • 1,669
  • 1
  • 12
  • 29
  • http://stackoverflow.com/a/31105859/3953764 – Piotr Skotnicki Oct 25 '16 at 10:24
  • If you use `c` as it was an associative containers, i.e. only call a member function that only exists in e.g. `std::map` or `std::unordered_map`, then you will get a compiler error if the caller passes something which does not have that member function. – Some programmer dude Oct 25 '16 at 10:28

2 Answers2

7

You can implement and use is_specialization_of as a generic solution:

template <typename, template <typename...> class>
struct is_specialization_of : std::false_type
{
};

template <template <typename...> class TTemplate, typename... Ts>
struct is_specialization_of<TTemplate<Ts...>, TTemplate> : std::true_type
{
};

template <typename container>
auto CopyKeyToVector(container c)
{
    static_assert(
        is_specialization_of<container, std::map>{} || 
        is_specialization_of<container, std::unordered_map>{}, "");
}

Full example on wandbox.


Note that it can also be used with std::enable_if:

template <typename T>
constexpr auto is_map_or_umap = 
    is_specialization_of<T, std::map>{} || 
    is_specialization_of<T, std::unordered_map>{};

template <typename container>
auto CopyKeyToVector(container) -> std::enable_if_t<is_map_or_umap<container>>
{
    // (1)
}

template <typename container>
auto CopyKeyToVector(container) -> std::enable_if_t<!is_map_or_umap<container>>
{
    // (2)
}

int main()
{
    CopyKeyToVector(std::map<int,float>{});           // Calls (1) 
    CopyKeyToVector(std::unordered_map<int,float>{}); // Calls (1)
    CopyKeyToVector(std::vector<int>{});              // Calls (2)  
}

Full example on wandbox.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
3

Don't use static assertions. Instead, add one level of indirection:

template <typename C>
void CopyKeyToVectorImpl(const C& c) { /* ... */ }

template <typename K, typename T, typename Pred, typename Alloc>
void CopyKeyToVector(const std::map<K, T, Pred, Alloc>& m) {
  CopyKeyToVectorImpl(m);
}

template <typename K, typename T, typename Hash, typename Eq, typename Alloc>
void CopyKeyToVector(const std::unordered_map<K, T, Hash, Eq, Alloc>& m) {
  CopyKeyToVectorImpl(m);
}

(You can modify the Impl function further to allow sets in the same wash by parametrising the value-to-key projection.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 2
    Won't the error message be a verbose "can't find overload" as opposed to the `static_assert`s concise "must pass map, unordered_map,..." message? – StoryTeller - Unslander Monica Oct 25 '16 at 10:32
  • @StoryTeller A compiler error in tandem with using a function named `CopyKeyToVector` should definitely give quite the clue, should it not? It could be a bit misleading if passing in say a `std::set` or the like, but I think it's probably clear enough. – erip Oct 25 '16 at 10:45
  • @erip, I disagree. But the subject of error message clarity is subjective, so I won't argue the point. – StoryTeller - Unslander Monica Oct 25 '16 at 11:32
  • @StoryTeller: well... depends how you see it. With my proposal, the supported overloads of `CopyKeyToVector` would appear in the publicly documented synopsis; the accepted set of types is part of the signature, not of the implementation. But indeed the diagnostic is different from what the OP asked for. – Kerrek SB Oct 25 '16 at 13:23