假设我们存储一个带有字符串键的结构,我们希望通过容器中的字符串找到它std::set
,所以常见的实现看起来像这样:
struct Foo {
std::string id;
};
struct FooComp {
using is_transparent = std::true_type;
bool operator()( const Foo &foo, const std::string &str ) const
{
return foo.id < str;
}
bool operator()( const std::string &str, const Foo &foo ) const
{
return str < foo.id;
}
bool operator()( const Foo &foo1, const Foo &foo2 ) const
{
return foo1.id < foo2.id;
}
};
std::set<Foo,FooComp> foo_set;
...
Run Code Online (Sandbox Code Playgroud)
这样可以正常工作,但是编写三种方法的FooComp
prety匹配相同(逻辑上)是单调的,容易出错.有没有办法最小化该代码?
您可以这样做:
struct Foo {
std::string id;
};
struct FooComp {
using is_transparent = std::true_type;
template <typename LHS, typename RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return ProjectAsId(lhs) < ProjectAsId(rhs);
}
private:
const std::string& ProjectAsId(const std::string& s) const { return s; }
const std::string& ProjectAsId(const Foo& foo) const { return foo.id; }
};
Run Code Online (Sandbox Code Playgroud)
您只需编写一次比较,但您必须为每种类型编写投影.
在C++ 17中,它甚至可以
template <auto f> struct ProjLess
{
using is_transparent = std::true_type;
template <typename LHS, typename RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return project(lhs) < project(rhs);
}
private:
template <typename T>
using f_t = decltype(std::invoke(f, std::declval<const T&>()));
template <typename T>
using is_f_callable = is_detected<f_t, T>;
template <typename T, std::enable_if_t<is_f_callable<T>::value>* = nullptr>
decltype(auto) project(const T& t) const { return std::invoke(f, t); }
template <typename T, std::enable_if_t<!is_f_callable<T>::value>* = nullptr>
const T& project(const T& t) const { return t; }
};
Run Code Online (Sandbox Code Playgroud)
用法:
std::set<Foo, ProjLess<&Foo::id>> s;
Run Code Online (Sandbox Code Playgroud)