命名運算子
你可以使用由標準 C++運算子引用的命名運算子來擴充套件 C++。
首先,我們從十幾個庫開始:
namespace named_operator {
template<class D>struct make_operator{constexpr make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
這還沒有做任何事情。
首先,附加向量
namespace my_ns {
struct append_t : named_operator::make_operator<append_t> {};
constexpr append_t append{};
template<class T, class A0, class A1>
std::vector<T, A0> named_invoke( std::vector<T, A0> lhs, append_t, std::vector<T, A1> const& rhs ) {
lhs.insert( lhs.end(), rhs.begin(), rhs.end() );
return std::move(lhs);
}
}
using my_ns::append;
std::vector<int> a {1,2,3};
std::vector<int> b {4,5,6};
auto c = a *append* b;
這裡的核心是我們定義 append_t:named_operator::make_operator<append_t>
型別的 append
物件。
然後我們在右側和左側為我們想要的型別過載 named_invoke(lhs,append_t,rhs)。
該庫過載 lhs*append_t
,返回一個臨時的 half_apply
物件。它也會超過 half_apply*rhs
來呼叫 named_invoke( lhs, append_t, rhs )
。
我們只需要建立正確的 append_t
令牌並執行適當簽名的 ADL 友好的 named_invoke
,並且所有內容都可以連線並執行。
對於更復雜的示例,假設你希望對 std::array 的元素進行逐元素乘法運算:
template<class=void, std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
return [](auto&& f) {
return f( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto indexer() { return indexer( std::make_index_sequence<N>{} ); }
namespace my_ns {
struct e_times_t : named_operator::make_operator<e_times_t> {};
constexpr e_times_t e_times{};
template<class L, class R, std::size_t N,
class Out=std::decay_t<decltype( std::declval<L const&>()*std::declval<R const&>() )>
>
std::array<Out, N> named_invoke( std::array<L, N> const& lhs, e_times_t, std::array<R, N> const& rhs ) {
using result_type = std::array<Out, N>;
auto index_over_N = indexer<N>();
return index_over_N([&](auto...is)->result_type {
return {{
(lhs[is] * rhs[is])...
}};
});
}
}
例項 。
如果你決定長度不匹配時該怎麼做,則可以擴充套件此元素陣列程式碼以處理元組或對或 C 樣式陣列,甚至可變長度容器。
你也可以使用元素運算子型別並獲取 lhs *element_wise<'+'>* rhs
。
編寫*dot*
和*cross*
產品運算子也是顯而易見的用途。
*
的使用可以擴充套件到支援其他分隔符,如+
。分隔符預測確定了命名運算子的預先確定性,這在將物理方程轉換為 C++時可能很重要,而最少使用額外的 ()
s。
在上面的庫中略有變化,我們可以支援 ->*then*
運算子並在標準更新之前擴充套件 std::function
,或者寫一個單一的 ->*bind*
。它也可以有一個有狀態的命名運算子,我們小心地將 Op
傳遞給最終的呼叫函式,允許:
named_operator<'*'> append = [](auto lhs, auto&& rhs) {
using std::begin; using std::end;
lhs.insert( end(lhs), begin(rhs), end(rhs) );
return std::move(lhs);
};
在 C++中生成一個命名的容器附加運算子 17。