我正在开发一个需要关联容器的C ++类(map
或unordered_map
)
我的班级看起来像这样:
template<typename T>
class myClass
{
static_assert(
std::is_copy_constructible<T>::value,
"content of myClass should be copy constructible"
);
// TODO: check if T::operator== exists
/* if hash<T> is available */
using container = std::unordered_map<T,T>;
/* if hash<T> is unavailable */
using container = std::map<T,T>;
public:
myClass() = default;
[...]
private:
container m_mapping;
}
我希望从模板T
自动推断出容器:
std::hash<T>
可用,我们应该使用unordered_map
来提高性能。std::hash<T>
不可用,我们应该回到map
有没有办法使用c ++模板?
答案 0 :(得分:6)
这可以通过一个相当简单的帮助模板来完成:
class A {
method() {
throw 'Error';
}
get try() {
return new Proxy(this, {
// Intercept method getter
get(target, name) {
if (typeof target[name] === 'function') {
return new Proxy(target[name], {
// Intercept method call
apply(target, self, args) {
try {
return target.apply(self, args);
} catch(e) {
// swallow error
}
}
})
}
return target[name];
}
});
}
}
const a = new A;
a.try.method(); // no error
a.method(); // throws error
如果template <typename T, typename = void>
struct helper
{
using type = std::map<T, T>;
};
template <typename T>
struct helper<T, std::void_t<decltype(std::hash<T>())>>
{
using type = std::unordered_map<T, T>;
};
格式正确,则选择该专业化,否则SFINAE会启动并使用基本模板。然后,您可以将std::hash<T>()
调整为如下所示:
myClass
请注意,在{+ C ++ 17>中添加了template<typename T>
class myClass
{
//...
using container = typename helper<T>::type;
//...
private:
//...
container m_mapping;
//...
};
。如果您无法访问C ++ 17,则可以将自己的std::void_t
实现为
void_t
答案 1 :(得分:2)
如果我说得对,那么在C ++ 11和更新版本中很容易实现。首先,您可以使用type_traits
标题中的条件类型。它看起来像这样:
std::conditional<test, Type1, Type2>
如果test
为真,则conditional
的内部类型type
等于Type1'. Otherwise, it has member type equal to
Type2`。
其次,我们需要上面的test
值,为此我们必须写一个特征。 我将从这里使用示例:How to decide if a template specialization exist - 让我们保持简单。
template<class T>
bool is_hashable_v = is_complete<std::hash<T>>::value;
编辑:上面不起作用。
我们先使用enable_if
:
template<class T, class = void>
struct is_hashable : std::false_type {};
template<class T>
struct is_hashable<
T,
typename std::enable_if<decltype(std::hash<T>())>::type
> : std::true_type {};
template<class T>
bool is_hashable_v = is_hashable<T>::value;
如果你有is_hashable_v
以上的话,那么就把它们放在一起:
using container =
typename std::conditional<
is_hashable_v<T>,
std::unordered_map<T, T>,
std::map<T, T>
>::type;
答案 2 :(得分:0)
我意识到std::hash<T>
不是静态函数,而是具有operator()
和构造函数的结构。而不是试图检查是否存在std::hash<T>()
(构造函数)或std::hash<T>()()
(散列函数),我们只需检查std::hash<T>
is_constructible
这使以下代码有效:
using container = typename std::conditional<
std::is_constructible<std::hash<T>>::value,
std::unordered_map<T,T>,
std::map<T,T>
>::type;
答案 3 :(得分:0)
借助@Miles Budnek解决方案,我建议:
template <typename Key, typename Value, typename = void>
struct associative_map
: std::map<Key, Value> {
using std::map<Key, Value>::map;
};
template <typename Key, typename Value>
struct associative_map<Key, Value, std::void_t<decltype(std::hash<Key>())>>
: std::unordered_map<Key, Value> {
using std::unordered_map<Key, Value>::unordered_map;
};
// Usage:
class Number {
int _i;
public:
Number(int i): _i(i) {}
operator int()const {return _i;}
};
int main() {
// associative_map below is std::unordered_map
associative_map<int, std::string> int2string = { {1, "one"} };
int2string[7] = "seven";
for(const auto& p : int2string) {
std::cout << p.first << ": " << p.second << std::endl;
}
// associative_map below is std::map
associative_map<Number, std::string> number2string = { {1, "one"} };
number2string[7] = "seven";
for(const auto& p : number2string) {
std::cout << p.first << ": " << p.second << std::endl;
}
}
答案 4 :(得分:0)
您可以使用概念:
template<typename Key>
concept Hashable = requires(Key a) {
{ std::hash<Key>{}(a) } -> std::convertible_to<std::size_t>;
};
template<typename Key>
concept Sortable = requires(Key a, Key b) {
{ a < b } -> std::convertible_to<bool>;
};
template<typename Key>
concept SortableNotHashable = Sortable<Key> && !Hashable<Key>;
template <typename Key, typename Value>
struct associative_map;
template <SortableNotHashable Key, typename Value>
struct associative_map<Key, Value>
: std::map<Key, Value> {
using std::map<Key, Value>::map;
};
template <Hashable Key, typename Value>
struct associative_map<Key, Value>
: std::unordered_map<Key, Value> {
using std::unordered_map<Key, Value>::unordered_map;
};
代码:https://godbolt.org/z/3GjuXt
template <typename Key, typename Value, typename = void>
struct associative_map
: std::map<Key, Value> {
using std::map<Key, Value>::map;
};
template <typename Key, typename Value>
struct associative_map<Key, Value, std::void_t<decltype(std::hash<Key>())>>
: std::unordered_map<Key, Value> {
using std::unordered_map<Key, Value>::unordered_map;
};
// Usage:
class Number {
int _i;
public:
Number(int i): _i(i) {}
operator int()const {return _i;}
};
int main() {
// associative_map below is std::unordered_map
associative_map<int, std::string> int2string = { {1, "one"} };
int2string[7] = "seven";
for(const auto& p : int2string) {
std::cout << p.first << ": " << p.second << std::endl;
}
// associative_map below is std::map
associative_map<Number, std::string> number2string = { {1, "one"} };
number2string[7] = "seven";
for(const auto& p : number2string) {
std::cout << p.first << ": " << p.second << std::endl;
}
}