web-dev-qa-db-ja.com

C ++でカスタムオペレーターを作成できますか?

このようなことができるようにカスタムオペレーターを作成することは可能ですか?

if ("Hello, world!" contains "Hello") ...

注:これは「次のことを行うのは良い考えですか?」とは別の質問です;)

40
Cogwheel

はい! (まあ、ちょっと)

あなたを助けるためにいくつかの公的に利用可能なツールがあります。どちらもプリプロセッサコード生成を使用して、カスタムオペレーターを実装するテンプレートを作成します。これらの演算子は、識別子と組み合わせた1つ以上の組み込み演算子で構成されています。

これらは実際にはカスタムオペレーターではなく、オペレーターのオーバーロードのトリックにすぎないため、いくつかの注意点があります。

  • マクロは悪です。間違えると、コンパイラーは問題を追跡するのにまったく役に立たなくなります。
  • マクロが正しくても、演算子の使用法または操作の定義にエラーがある場合、コンパイラーは少しだけ役に立ちます。
  • 演算子の一部として有効な識別子を使用する必要があります。より記号のような演算子が必要な場合は、_oまたは同様の単​​純な英数字を使用できます。

CustomOperators

私がこの目的のために自分のライブラリで作業しているときに(下記を参照)、このプロジェクトに出くわしました。 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)

IdOp

純粋な軽薄さでの運動 として始まったものが、この問題に対する私自身の見方となりました。同様の例を次に示します。

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_>

主な違い

  • CustomOperatorsは後置単項演算子をサポートします
  • IdOpテンプレートは、ポインターではなく参照を使用して、フリーストアの使用を排除し、操作のコンパイル時の完全な評価を可能にします
  • IdOpを使用すると、同じルート識別子に対して複数の操作を簡単に指定できます
35
Cogwheel

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;
}
12
John P

もう少し正確にするために、C++ itselfは、既存の操作の新しいオーバーロードの作成のみをサポートし、新しい演算子の作成はサポートしていません。まったく新しい演算子を作成できる言語(MLなどの子孫のほとんど)がありますが、C++はその1つではありません。

(少なくとも)他の回答で言及されているCustomOperatorsライブラリは、完全にカスタムオペレーターもサポートしていません。少なくとも私が正しく読んでいるのであれば、それは(内部で)カスタムオペレーターを既存のオペレーターのオーバーロードに変換することです。これにより、柔軟性がいくらか犠牲になりますが、簡単になります。たとえば、MLで新しい演算子を作成すると、組み込み演算子とは異なる優先順位を付けることができます。

2
Jerry Coffin

次の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
1
Davide Cannizzo

技術的には違います。つまり、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名、またはテンプレート名にすることはできません。関数名にすることはできません。それは本当に唯一のマクロであり得、それは唯一の残りの名前付きエンティティです。それについては、「はい(そうですね)」の回答を参照してください。

0
MSalters