Home > Back-end >  C skip template instantiation for cases that don't satisfy requires clause
C skip template instantiation for cases that don't satisfy requires clause

Time:06-07

I'm writing something like STL red-black tree for practicing coding.

My TreeSet, TreeMap, TreeMultiSet, TreeMultiMap all share implementation of RedBlackTree, whose declaration is like this:

template <Containable K, typename V, typename Comp, bool AllowDup>
requires std::invocable<Comp, K, K>
class RedBlackTree {
   // ...
};

template <Containable K, typename Comp = std::less<K>>
using TreeSet = RedBlackTree<K, K, Comp, false>;

template <Containable K, typename Comp = std::less<K>>
using TreeMultiSet = RedBlackTree<K, K, Comp, true>;

template <Containable K, Containable V, typename Comp = std::less<K>>
using TreeMap = RedBlackTree<K, std::pair<const K, V>, Comp, false>;

template <Containable K, Containable V, typename Comp = std::less<K>>
using TreeMultiMap = RedBlackTree<K, std::pair<const K, V>, Comp, true>;

I have a problem when writing operator[], which should be instantiated only for TreeMap (this is the same as STL that provides operator[] only for std::map among four ordered associative containers)

My declaration is like this:

template <typename T>
  std::add_lvalue_reference_t<decltype(std::declval<V>().second)>
  operator[](T&& raw_key) requires (!std::is_same_v<K, V> && !AllowDup)

But it fails to compile if RedBlackTree was instantiated as TreeSet (where V = K so !std::is_same_v<K, V> does not hold).

The compiler's complain is that V = K = int (because I instantiated RedBlackTree as TreeSet<int>), so V does not have second.

Why the compiler doesn't skip instantiation of this function? requires clause isn't satisfied...

Compiler: MSVC 19.30

CodePudding user response:

V is fixed by the class, so you cannot use SFINAE on it in member function.

You might introduce extra template parameter as workaround:

template <typename V2 = V, typename T>
std::add_lvalue_reference_t<typename V2::second_type>
operator[](T&& raw_key) requires (!std::is_same_v<K, V> && !AllowDup);

or use auto/decltype(auto)

template <typename V2 = V, typename T>
auto& operator[](T&& raw_key) requires (!std::is_same_v<K, V> && !AllowDup);

CodePudding user response:

Self-answer:

I found that this solves the problem:

template <typename T>
  auto& operator[](T&& raw_key) requires (!std::is_same_v<K, V> && !AllowDup)
  • Related