#define a special operator in c++

2019-05-07 15:36发布

问题:

Say that I want to make up a special operator !+ in C++ between two objects. I would like to use !+, on example, because I think it is much more meaningful than any other operator.

One basic thing I could do is to find a free, unused operator and make the replacement work with a #define:

#define !+ %
class myclass 
{
  public:
   int operator %(myclass &c)
   {
      return 3;
   }
}

So that if I later write something like

a!+b 

with a and b instances of myclass, it would work.

Now, is there any way to define it instead that with an operator, with some function? Something like:

#define a!+b -> a.operatorexclamativeplus(b)

It would make the translation much less dirty, and would allow for a potentially unlimited number of these new "fake operators"!

回答1:

"Now, is there any way to define it instead that with an operator, with some function?"

No, !+ doesn't form a valid (normal) identifier for a preprocessor macro. The characters are reserved for language intrinsic operators.

The set of characters you can use for macro definitions is [_A-Za-z][_A-Za-z0-9]*regex syntax1.

As from the c++ standards definitionsdraft section

16 Preprocessing directives

...
control-line:
...
# define identifier replacement-list new-line
# define identifier lparen identifier-listopt) replacement-list new-line
# define identifier lparen ... ) replacement-list new-line
# define identifier lparen identifier-list, ... ) replacement-list new-line


UPDATE:
I've been experimenting with this a bit, since this actually is an interesting question, and if not #define'd macros, we can use overloading intrinsic operator functions for classes in c++.

I have found there actually are possible (but may be weird and unexpectedly behaving) options, to overload combinations of intrinsic operators by chaining them and keep state. The closest thing you can get for your newly introduced operator could be:

class Foo {
    enum OpState { None , PlusState , NotState };   
public:
   Foo& operator+() {
       cout << "operator+()" << endl;
       opState = PlusState;
       return *this;
   }
   Foo& operator!() {
       cout << "operator!()" << endl;
       switch(opState) {
       case PlusState:
           operatorexclamativeplus();
           break;       
       default:
           opState = NotState;
           break;       
       }
       return *this;
   }    
private:
    Foo& operatorexclamativeplus() {
       cout << "operatorexclamativeplus()" << endl;
       opState = None;
       return *this;
    }
    Foo& operatorexclamativeplus(const Foo& rhs) {
       cout << "operatorexclamativeplus(const Foo& rhs)" << endl;
       opState = None;
       return *this;
    }   
    OpState opState;
};

int main() {
    Foo x;
    Foo z = !+x;
    return 0;
}

Output

operator+()
operator!()
operatorexclamativeplus()

See live sample.


Though, because of operator precedence rules, this would work only for unary operators (! has higher precedence than binary +), the form you want to have doesn't seem to be actually achievable:

class Foo {
    // ...
public:
    // Provide an additional binary 'operator+()'
    friend Foo& operator+(Foo& lhs, const Foo& rhs) {
        cout << "operator+(Foo& lhs, const Foo& rhs)" << endl;
        if(lhs.opState == NotState) {
            return lhs.operatorexclamativeplus(rhs);
        }
        return lhs;
    }
    // ...
};

int main() {
    Foo x;
    Foo y;
    Foo z = y !+ x;
    return 0;
}

See this miserably fail.


CONCLUSION:

  1. Some overloaded combinations of intrinsic operators might be syntactically possible, regarding their precedence definitions, and maintaining state of lvalues.
  2. It's probably not a very good idea, to try to overload intrinsic operator behavior, introducing completely new semantics.

______________________________________________________________________________________

1) Regarding leading underscores (_,__) for identifiers please read the standards sections mentioned in this answer, these are syntactically valid though.



回答2:

Technically, C++ does not let you define new operators. However, you can get any behavior you want out of existing operators, including the appearance of having a new operator.
(Not saying it's a good idea, just answering the question "Is it possible?")

You can not ever write a !+ b, becuase ! is not a binary operator.
But you can write a +! b because + is binary and ! is unary.
And you can redefine those operators so that a +! b returns what you want.

The trick is that you define the unary operator to return a helper object, and then define the binary operator to act upon that helper object.

As an example, here is code that will create the appearance of a +! operator. This new +! operator returns ten times the left hand side plus the right hand side. So... 1 +! 2 would yield 12.

class MyClass
{
public:
    int value;
};

class MyClassHelper
{
public:
    int value;
};

// define a unary ! operator that takes a right hand MyClass object
const MyClassHelper operator!(const MyClass right)
{
    // this operator just copies the value into a MyClassHelper object
    MyClassHelper answer;
    answer.value = right.value;
    return answer;
}

// define a binary + operator that takes a left hand MyClass object and a right hand MyClassHelper object
const MyClass operator+(const MyClass left, const MyClassHelper right)
{
    // this operator should return the value you want the +! operator to return
    MyClass answer;
    answer.value = left.value * 10 + right.value;
    return answer;
}

int main()
{
    // initialize a and b
    MyClass a, b;
    a.value = 1;
    b.value = 2;

    // this line will now compile with the +! operator
    MyClass c;
    c = a +! b;
    cout << "c = " << c.value << endl;

    return 0;
}


回答3:

You cannot define new operators in C/C++. The standard disallows it, and the metaprogramming capabilities of macros and templates are not powerful enough to get around it.

But did that stop Brad Cox (Objecte-C) and Bjorne Stroustrup (C++/cfront)? Of course not! Bertrand Meyer did it too (Eiffel) and so did Kernighan and Plauger (RATFOR).

They way you add new language features such as new operators to C/C++ is by creating your own pre-processor. You can use a macro processing language or write your own compiler so that the input:

if (something) {
  a!+b
}

Is translated into:

if (something) {
  a.operatorexclamativeplus(b);
}

And the C++ compiler is perfectly happy.

You never know, your new language might even catch on. Sometimes they do.



回答4:

It is not possible to define your own unique operators in C++.

I also second that it might be possible in other languages to do so.



回答5:

Others explained about the impossibility of this. However, you also asked about making this a function, and yes, that's' the way I would go first. You simply put whatever operation you want into a (free/unbound) function, giving it a suitable name, which will not cause any confusion to the reader.

Now, if you decided that your special case is special enough to break the rules, then you could do the following:

int x = 23 /strangeop/ 42;

Here, strangeop is actually an object and there are overloaded / operators for objects of this type and integers. The first such operator evaluates to a proxy object which keeps a reference to the integer operand but also defines overloaded / operators in which it receives the other operand and then returns the actual result of the strange operation. Note here that you could use any binary operator here, it's more a matter of personal choice.

Notes:

  • You can take this further by #define STRANGEOP /strangeop/.
  • Using this, you can also define unary strange operators, you will still need a binary operator for overloading though.
  • Compare this with strange_op(23, 43) and consider it for its better readability.
  • I haven't found a case where I really need this technique, so I can't tell you if it works and where its practical limits are.