このようなことができるようにカスタムオペレーターを作成することは可能ですか?
if ("Hello, world!" contains "Hello") ...
注:これは「次のことを行うのは良い考えですか?」とは別の質問です;)
あなたを助けるためにいくつかの公的に利用可能なツールがあります。どちらもプリプロセッサコード生成を使用して、カスタムオペレーターを実装するテンプレートを作成します。これらの演算子は、識別子と組み合わせた1つ以上の組み込み演算子で構成されています。
これらは実際にはカスタムオペレーターではなく、オペレーターのオーバーロードのトリックにすぎないため、いくつかの注意点があります。
_
、o
または同様の単純な英数字を使用できます。私がこの目的のために自分のライブラリで作業しているときに(下記を参照)、このプロジェクトに出くわしました。 avg
演算子を作成する例を次に示します。
#define avg BinaryOperatorDefinition(_op_avg, /)
DeclareBinaryOperator(_op_avg)
DeclareOperatorLeftType(_op_avg, /, double);
inline double _op_avg(double l, double r)
{
return (l + r) / 2;
}
BindBinaryOperator(double, _op_avg, /, double, double)
純粋な軽薄さでの運動 として始まったものが、この問題に対する私自身の見方となりました。同様の例を次に示します。
template<typename T> class AvgOp {
public:
T operator()(const T& left, const T& right)
{
return (left + right) / 2;
}
};
IDOP_CREATE_LEFT_HANDED(<, _avg_, >, AvgOp)
#define avg <_avg_>
Sander Stoksが 'Syntactic Aspartame' で徹底的に検討しているメソッドがあり、次の形式を使用できます。
if ("Hello, world!" <contains> "Hello") ...
本質的に、演算子 '<'と '>'がオーバーロードされたプロキシオブジェクトが必要です。プロキシはすべての作業を行います。 'contains'は、それ自体の動作やデータを持たないシングルトンにすることができます。
// Not my code!
const struct contains_ {} contains;
template <typename T>
struct ContainsProxy
{
ContainsProxy(const T& t): t_(t) {}
const T& t_;
};
template <typename T>
ContainsProxy<T> operator<(const T& lhs, const contains_& rhs)
{
return ContainsProxy<T>(lhs);
}
bool operator>(const ContainsProxy<Rect>& lhs, const Rect& rhs)
{
return lhs.t_.left <= rhs.left &&
lhs.t_.top <= rhs.top &&
lhs.t_.right >= rhs.right &&
lhs.t_.bottom >= rhs.bottom;
}
もう少し正確にするために、C++ itselfは、既存の操作の新しいオーバーロードの作成のみをサポートし、新しい演算子の作成はサポートしていません。まったく新しい演算子を作成できる言語(MLなどの子孫のほとんど)がありますが、C++はその1つではありません。
(少なくとも)他の回答で言及されているCustomOperatorsライブラリは、完全にカスタムオペレーターもサポートしていません。少なくとも私が正しく読んでいるのであれば、それは(内部で)カスタムオペレーターを既存のオペレーターのオーバーロードに変換することです。これにより、柔軟性がいくらか犠牲になりますが、簡単になります。たとえば、MLで新しい演算子を作成すると、組み込み演算子とは異なる優先順位を付けることができます。
次の2つのマクロを作成しました。
#define define const struct
#define operator(ReturnType, OperatorName, FirstOperandType, SecondOperandType) OperatorName ## _ {} OperatorName; template <typename T> struct OperatorName ## Proxy{public:OperatorName ## Proxy(const T& t) : t_(t){}const T& t_;static ReturnType _ ## OperatorName ## _(const FirstOperandType a, const SecondOperandType b);};template <typename T> OperatorName ## Proxy<T> operator<(const T& lhs, const OperatorName ## _& rhs){return OperatorName ## Proxy<T>(lhs);}ReturnType operator>(const OperatorName ## Proxy<FirstOperandType>& lhs, const SecondOperandType& rhs){return OperatorName ## Proxy<FirstOperandType>::_ ## OperatorName ## _(lhs.t_, rhs);}template <typename T> inline ReturnType OperatorName ## Proxy<T>::_ ## OperatorName ## _(const FirstOperandType a, const SecondOperandType b)
次に、次の例のようにカスタムオペレーターを定義するだけです。
define operator(bool, myOr, bool, bool) { // Arguments are the return type, the name of the operator, the left operand type and the right operand type, respectively
return a || b;
}
#define myOr <myOr> // Finally, you have to define a macro to avoid to put the < and > operator at the start and end of the operator name
オペレーターをセットアップしたら、事前定義されたオペレーターとして使用できます。
bool a = true myOr false;
// a == true
技術的には違います。つまり、operator+
、operator-
などのセットを拡張することはできません。しかし、あなたの例で提案しているのは別のものです。 string-literal "contains" string-literal
が重要なロジックを含む式(#define contains ""
は重要なケース)であるような "contains"の定義があるかどうか疑問に思っています。
string-literal X string-literal
という形式の式はあまりありません。これは、文字列リテラル自体が式であるためです。したがって、expr X expr
という形式の言語ルールを探しています。それらのかなりの数がありますが、それらはすべて演算子のルールであり、それらは文字列では機能しません。明らかな実装にもかかわらず、"Hello, " + "world"
は有効な式ではありません。それで、Xはstring-literal X string-literal
に他に何があるでしょうか?それ自体を表現にすることはできません。 typename、typedef名、またはテンプレート名にすることはできません。関数名にすることはできません。それは本当に唯一のマクロであり得、それは唯一の残りの名前付きエンティティです。それについては、「はい(そうですね)」の回答を参照してください。