我正在写一些类似STL红黑树的东西来练习编码。
我的TreeSet
,TreeMap
,TreeMultiSet
,TreeMultiMap
都共享RedBlackTree
的实现,其声明如下:
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>;
我在编写operator[]
时遇到了一个问题,它应该仅为TreeMap
实例化(这与STL相同,仅为四个有序关联容器中的std::map
提供operator[]
)
我的声明是这样的:
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)
但是如果RedBlackTree
被实例化为TreeSet
(其中V = K
所以!std::is_same_v<K, V>
不成立),则编译失败。
编译器的抱怨是V = K = int
(因为我将RedBlackTree
实例化为TreeSet<int>
),所以V
没有second
。
为什么编译器不跳过这个函数的实例化?requires
条款不满足…
编译器:MSVC 19.30
V
是由类固定的,所以你不能在成员函数中对它使用SFINAE。
你可以引入额外的模板参数作为解决方法:
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);
或使用auto
/decltype(auto)
template <typename V2 = V, typename T>
auto& operator[](T&& raw_key) requires (!std::is_same_v<K, V> && !AllowDup);
自我回答:
我发现这个解决了问题:
template <typename T>
auto& operator[](T&& raw_key) requires (!std::is_same_v<K, V> && !AllowDup)