透明比较器代码最小化

Sla*_*ica 6 c++ c++14

假设我们存储一个带有字符串键的结构,我们希望通过容器中的字符串找到它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)

这样可以正常工作,但是编写三种方法的FooCompprety匹配相同(逻辑上)是单调的,容易出错.有没有办法最小化该代码?

Jar*_*d42 9

您可以这样做:

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)

用C++ 1z演示