web-dev-qa-db-ja.com

可換演算の定義中にコードの重複を減らす

overlapという名前の可換バイナリ関数のオーバーロードのセットがあり、2つの異なる型を受け入れます。

_class A a; class B b;
bool overlap(A, B);
bool overlap(B, A);
_

私の関数overlapは、1つの形状が他の形状と重なる場合にのみtrueを返します。これは、 multimethods を説明するときに使用される一般的な例です。

overlap(a, b)overlap(b, a)と同等であるため、リレーションの1つの「サイド」のみを実装する必要があります。反復的な解決策の1つは、次のようなものを書くことです。

_bool overlap(A a, B b) { /* check for overlap */ }
bool overlap(B b, A a) { return overlap(a, b);   }
_

しかし、テンプレートを使用して代わりに生成できるようにすることで、同じ関数の余分な_N! / 2_自明なバージョンを記述しないことを望みます。

_template <typename T, typename U> 
bool overlap(T&& t, U&& u) 
{ return overlap(std::forward<U>(u), std::forward<T>(t)); }
_

残念なことに、これは無限に再帰する傾向があり、これは受け入れられません: http://coliru.stacked-crooked.com/a/20851835593bd557 を参照してください

このような無限再帰を防ぐにはどうすればよいですか?問題に正しく近づいていますか?

50
mbozzi

簡単な修正を次に示します。

template <typename T, typename U> 
void overlap(T t, U u)
{
    void overlap(U, T);
    overlap(u, t);
}

テンプレート自体はターゲット関数を宣言しますが、完全一致であるため、再帰よりも優先されます(実際の場合は、一貫性と参照性に注意してください)。関数が実装されていない場合、リンカーエラーが発生します。

/tmp/cc7zinK8.o: In function `void overlap<C, D>(C, D)':
main.cpp:(.text._Z7overlapI1C1DEvT_T0_[_Z7overlapI1C1DEvT_T0_]+0x20):
    undefined reference to `overlap(D, C)'
collect2: error: ld returned 1 exit status

...欠落している関数を直接指します:)

64
Quentin

賢者がかつて言ったように、間接的な層が多すぎることを除いて、間接的な層を追加しても解決できない問題はありません。

そのため、SFINAEといくつかのインダイレクションを使用して実行します。

template<class A, class B>
auto overlap(A&& a, B&& b)
-> decltype(overlap_impl('\0', std::forward<A>(a), std::forward<B>(b)))
{ return overlap_impl('\0', std::forward<A>(a), std::forward<B>(b)); }

template<class A, class B>
auto overlap_impl(int, A&& a, B&& b)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(b)); }

template<class A, class B>
auto overlap_impl(long, B&& b, A&& a)
-> decltype(do_overlap(std::forward<A>(a), std::forward<B>(b)))
{ return do_overlap(std::forward<A>(a), std::forward<B>(b)); }

// You can provide more choices if you want, for example to use member-functions.

// Implement `do_overlap(A, B)`, maybe with references, in at least one direction.
13
Deduplicator

実際のメソッドの名前をoverlap_implのようなものに変更し、テンプレート内でこれを呼び出すことができます。再帰を解除します。

bool overlap_impl(A a, B b) { /* check for overlap */ }

template <typename T, typename U> 
bool overlap(T&& t, U&& u) 
{ return overlap_impl(std::forward<U>(u), std::forward<T>(t)); }

template<> bool overlap(A&& t, B&& u)
{ return overlap_impl(std::forward<A>(t), std::forward<B>(u)); }
1
K. Kirsz