Мое решение все в классе:
struct FooComp {
using is_transparent = std::true_type;
struct FooProj {
std::string const& str;
FooProj(std::string const& sin):str(sin) {}
FooProj(const Foo& foo):str(foo.id) {}
FooProj(FooProj const&) = default;
friend bool operator<(FooProj lhs, FooProj rhs) {
return lhs.str < rhs.str;
}
};
bool operator()(FooProj lhs, FooProj rhs) const
{
return lhs<rhs;
}
};
Это не поддерживает t, которые могут конвертироваться в std::string
.
Однако при выполнении проекции на основе сравнения, я это сделать:
template<class F, class After=std::less<>>
auto order_by(F&& f, After&& after={}) {
return
[f=std::forward<F>(f), after=std::forward<After>(after)]
(auto&& rhs, auto&&lhs)->bool {
return after(f(decltype(lhs)(lhs)), f(decltype(rhs)(rhs)));
};
}
который принимает проекцию и генерирует функцию сравнения для него. Мы делаем его прозрачным с:
template<class F>
struct as_transparent_t {
F f;
using is_transparent=std::true_type;
template<class Lhs, class Rhs>
bool operator(Lhs const& lhs, Rhs const& rhs)const{ return f(lhs, rhs); }
};
template<class F>
as_transparent_f<std::decay_t<F>>
as_transparent(F&& f) { return {std::forward<F>(f)}; }
поэтому мы можем спроектировать и быть прозрачным с помощью:
as_transparent(order_by(some_projection));
который только оставляет проекцию.
В C++ 14, мы просто делаем
std::string const& foo_proj_f(std::string const& str) { return str; }
std::string const& foo_proj_f(Foo const& foo) { return foo.id; }
auto foo_proj = [](auto const& x)->decltype(auto){ return foo_proj_f(x); };
auto foo_order = as_transparent(order_by(foo_proj));
, который разрушает вещи вниз на модульные куски.
В C++ 17 можно использовать if constexpr
:
auto foo_proj = [](auto const& x)->std::string const& {
if constexpr(std::is_same<decltype(x), std::string const&>{}) {
return x;
}
if constexpr(std::is_same<decltype(x), Foo const&>{}) {
return x.id;
}
};
auto foo_order = as_transparent(order_by(foo_proj));
или
template<class...Ts>
struct overloaded:Ts...{
using Ts::operator()...;
overloaded(Ts...ts):Ts(std::move(ts)...){}
};
template<class...Ts> overloaded -> overloaded<Ts...>;
который позволяет
auto foo_proj = overloaded{
[](std::string const& s)->decltype(auto){return s;},
[](Foo const& f)->decltype(auto){return f.id;}
};
, который может быть легче читать, чем версия if constexpr
. (Эта версия также может быть адаптирована к c++14 или c++11).
с помощью шаблона? (просто случайное предположение :) – user1810087
@Slava Достаточно объявить конструктор преобразования для структуры. –
@ VladfromMoscow, но у этого были бы накладные расходы для нетривиальной структуры 'Foo'? Так что это не совсем равное решение.Конечно, это не значит, что это не одно из решений. Вероятно, вы должны ответить на этот вопрос. – Slava