コメントしたように、この難問を解決する最もクリーンな方法は、クライアントが外部使用のために動作をオーバーライドboost::variant<>
できるようにするオペレーター ポリシー (実際にはオペレーターごと) を使用しての実装を強化することだと思います。(明らかに、それは多くの一般的なプログラミング作業です)。
回避策を実装しました。これにより、 で実装されている場合でも、バリアントのカスタム オペレータを実装できますboost/variant.hpp
。
私の脳波は使用することでしたBOOST_STRONG_TYPEDEF
。
アイデアは、別の実際の型のバリアントを作成することによって、オーバーロードの解決を破る (または、少なくともカスタム オーバーロードを優先する解決にする) ことです (これは、「絶望的な」ADL バリアを少し思い出させます: スコープから名前を非表示にすることはできません)using
。 、競合する宣言がクラスの名前空間自体に存在するため、「非武装化された名前空間」(障壁) に移動することはできませんが、それらを「おとり」型に適用しないようにすることはできます)。
残念ながら、これは and ファミリではうまく機能しませんoperator<
。なぜなら、boost strong-typedef は実際には、'base' 型で (弱い) 全体的な順序付けセマンティクスを保持するために懸命に機能するからです。通常の英語では、強力な typedef も定義operator<
されます(基本型の実装への委譲)。
心配する必要はありません。CUSTOM_STRONG_TYPEDEF を実行して、楽しく作業を進めることができます。概念実証のために main のテスト ケースを参照してください (以下の出力)。
興味深いインタラクションが説明されているため、このデモを選択しましたが、バリアント型をoperator<
カスタム化する上で邪魔になるものは何もないと思います。operator==
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>
/////////////////////////////////////////////////////
// copied and reduced from boost/strong_typedef.hpp
#define CUSTOM_STRONG_TYPEDEF(T, D) \
struct D \
/*: boost::totally_ordered1< D */ \
/*, boost::totally_ordered2< D, T */ \
/*> > */ \
{ \
T t; \
explicit D(const T t_) : t(t_) {}; \
D(){}; \
D(const D & t_) : t(t_.t){} \
D & operator=(const D & rhs) { t = rhs.t; return *this;} \
D & operator=(const T & rhs) { t = rhs; return *this;} \
operator const T & () const {return t; } \
operator T & () { return t; } \
/*bool operator==(const D & rhs) const { return t == rhs.t; } */\
/*bool operator<(const D & rhs) const { return t < rhs.t; } */\
};
namespace detail
{
typedef boost::variant<unsigned int, std::string> variant_t;
struct less_visitor : boost::static_visitor<bool>
{
bool operator()(const std::string& a, int b) const
{ return boost::lexical_cast<int>(a) < b; }
bool operator()(int a, const std::string& b) const
{ return a < boost::lexical_cast<int>(b); }
template <typename T>
bool operator()(const T& a, const T& b) const
{ return a < b; }
};
struct variant_less
{
less_visitor _helper;
bool operator()(const variant_t& a, const variant_t& b) const
{ return boost::apply_visitor(_helper, a, b); }
};
}
CUSTOM_STRONG_TYPEDEF(detail::variant_t, custom_vt);
namespace
{
bool operator<(const custom_vt& a, const custom_vt& b)
{ return detail::variant_less()(a, b); }
std::ostream& operator<<(std::ostream& os, const custom_vt& v)
{ return os << (const detail::variant_t&)v; }
}
int main()
{
const detail::variant_t I(43), S("42");
const custom_vt i(I), s(S);
// regression test (compare to boost behaviour)
std::cout << "boost: " << I << " < " << S << ": " << std::boolalpha << (I<S) << "\n";
std::cout << "boost: " << S << " < " << I << ": " << std::boolalpha << (S<I) << "\n";
// FIX1: clumsy syntax (works for boost native variants)
detail::variant_less pred;
std::cout << "clumsy: " << i << " < " << s << ": " << std::boolalpha << pred(i,s) << "\n";
std::cout << "clumsy: " << s << " < " << i << ": " << std::boolalpha << pred(s,i) << "\n";
std::cout << "clumsy: " << I << " < " << S << ": " << std::boolalpha << pred(I,S) << "\n";
std::cout << "clumsy: " << S << " < " << I << ": " << std::boolalpha << pred(S,I) << "\n";
// FIX2: neat syntax (requires a custom type wrapper)
std::cout << "custom: " << i << " < " << s << ": " << std::boolalpha << (i<s) << "\n";
std::cout << "custom: " << s << " < " << i << ": " << std::boolalpha << (s<i) << "\n";
}
出力:
boost: 43 < 42: true
boost: 42 < 43: false
clumsy: 43 < 42: false
clumsy: 42 < 43: true
clumsy: 43 < 42: false
clumsy: 42 < 43: true
custom: 43 < 42: false
custom: 42 < 43: true
もちろん、TMP を使用してバリアントに作用するライブラリ API に custom_vt を渡したい場合は、残念な相互作用が発生する可能性があります。ただし、この 2 つの間の変換は簡単なので、detail::variant_t を適切なタイミングで使用することで「自分のやり方で戦う」ことができるはずです。
これは、呼び出しサイトで構文上の利便性を得るために支払わなければならない代償です。