ตัวดำเนินการ Unary plus ทำอะไร?


87

ตัวดำเนินการ Unary plus ทำอะไร? มีคำจำกัดความหลายอย่างที่ฉันพบ ( ที่นี่และที่นี่ ) แต่ฉันยังไม่รู้ว่าจะใช้ทำอะไร ดูเหมือนว่ามันจะไม่ได้ทำอะไรเลย แต่มันมีเหตุผลใช่มั้ย?


3
ฉันคิดว่าเหตุผลทางประวัติศาสตร์ถูกประเมินต่ำไปอย่างมาก
Wolf

3
คำถามที่มีแท็กภาษาต่างกันสามแท็กค่อนข้างคลุมเครือ / ไม่ดี! คำตอบนั้นแตกต่างกันมากสำหรับ C # (โอเวอร์โหลด) และ C (ไม่โอเวอร์โหลด) และ C ++ (โอเวอร์โหลดได้ แต่รับค่านี้มาจาก C และด้วยเหตุนี้คุณสมบัติของมันด้วย)
legends2k

ฉันต้องการเพิ่มลิงก์ไปยังข้อบกพร่อง 10 อันดับแรกของ Eric Lippert ของ C # คุณจะพบตัวดำเนินการ unary plus ในการกล่าวถึงที่มีเกียรติ: informit.com/articles/article.aspx?p=2425867โดยพื้นฐานแล้วเขาบอกว่าควรลบออกหรือไม่ควรเพิ่ม
Noel Widmer

คำตอบ:


57

จะมีงานล้นมือหากคุณรู้สึกว่าต้องการ สำหรับทุกประเภทที่กำหนดไว้ล่วงหน้าโดยพื้นฐานแล้วจะไม่มีการดำเนินการใด ๆ

การใช้ตัวดำเนินการเลขคณิตยูนารีแบบ no-op ในทางปฏิบัตินั้นค่อนข้าง จำกัด และมีแนวโน้มที่จะเกี่ยวข้องกับผลของการใช้ค่าในนิพจน์ทางคณิตศาสตร์แทนที่จะเป็นตัวดำเนินการเอง ตัวอย่างเช่นสามารถใช้เพื่อบังคับให้ขยับขยายจากประเภทปริพันธ์ที่เล็กกว่าไปintเป็นหรือตรวจสอบให้แน่ใจว่าผลลัพธ์ของนิพจน์ถือเป็นค่า rvalue ดังนั้นจึงไม่เข้ากันได้กับconstพารามิเตอร์ที่ไม่อ้างอิง อย่างไรก็ตามฉันขอส่งว่าการใช้งานเหล่านี้เหมาะกับโค้ดกอล์ฟมากกว่าความสามารถในการอ่าน :-)


7
สิ่งนี้ไม่เป็นความจริงดังตัวอย่างด้านล่างนี้
jkerian

3
โอเคฉันจะซื้อว่ามันมีประโยชน์บ้าง แต่การใช้งานส่วนใหญ่ที่กล่าวถึงด้านล่างเกี่ยวข้องกับความจริงที่ว่ามันเป็นนิพจน์ตัวเลขที่สร้างขึ้นโดยใช้โอเปอเรเตอร์ no-op ตัวอย่างเช่นการส่งเสริมintและส่งผลให้ค่า rvalue เป็นผลของการแสดงออก -ness ไม่ใช่ของตัวดำเนินการ + เอง
Jeffrey Hantin

119

ที่จริงเอกบวกไม่ทำอะไรสักอย่าง - แม้ในซีดำเนินการแปลงทางคณิตศาสตร์ตามปกติในตัวถูกดำเนินการและส่งกลับค่าใหม่ซึ่งสามารถเป็นจำนวนเต็มของความกว้างมากขึ้น หากค่าเดิมเป็นจำนวนเต็มที่ไม่มีเครื่องหมายซึ่งมีความกว้างน้อยกว่าค่าintนั้นจะเปลี่ยนเป็นsignedค่าเช่นกัน

โดยปกติสิ่งนี้ไม่สำคัญนัก แต่อาจมีผลได้ดังนั้นจึงไม่ควรใช้ยูนารีพลัสเป็น "ความคิดเห็น" ที่แสดงว่าจำนวนเต็มเป็นค่าบวก พิจารณาโปรแกรม C ++ ต่อไปนี้:

void foo(unsigned short x)
{
 std::cout << "x is an unsigned short" << std::endl;
}

void foo(int x)
{
 std::cout << "x is an int" << std::endl;
}

int main()
{
 unsigned short x = 5;
 foo(+x);
}

สิ่งนี้จะแสดง "x คือ int"

ดังนั้นในตัวอย่างนี้ unary plus จึงสร้างค่าใหม่ด้วยประเภทและการลงนามที่แตกต่างกัน


5
หากคุณกำลังเขียนโค้ดที่ให้ผลลัพธ์ที่กำหนดเมื่อให้สั้นและ int ที่เปรียบเทียบเท่ากันคุณอาจมีปัญหาอื่น
Lie Ryan

1
ขอบคุณที่ทำให้ชัดเจน (ข้อคิดเห็น: ใน C ฉันไม่สามารถใช้ตัวอย่างในการทำงานได้เนื่องจากฉันไม่สามารถใช้ฟังก์ชัน foo มากเกินไป)
Binarian

พูดว่าan integer of greater widthอะไร?
yekanchi

ในบริบทนี้ "width" หมายถึงจำนวนไบต์ของหน่วยเก็บข้อมูลที่เกี่ยวข้องกับค่า
ไจล์ส

41

จาก K&R ฉบับที่สอง:

unary + เป็นของใหม่ที่มีมาตรฐาน ANSI มันถูกเพิ่มเพื่อความสมมาตรด้วยยูนารี -


6
อย่างแน่นอน * นี่คือเหตุผลทางประวัติศาสตร์ C ++ ต้องปรับตัวดังนั้นจึงต้องรับมือกับการโอเวอร์โหลด
Wolf

38

ฉันเคยเห็นมันใช้เพื่อความชัดเจนเพื่อเน้นค่าบวกที่แตกต่างจากค่าลบ:

shift(+1);
shift(-1);

แต่นั่นเป็นการใช้งานที่ค่อนข้างอ่อนแอ คำตอบคือเกินกำลังแน่นอน


9
ฉันได้ทำสิ่งนี้เช่นกันโดยเฉพาะอย่างยิ่งเมื่อจัดการกับเวกเตอร์เช่นvec3(+1,-1,-1)
mpen

28

สิ่งหนึ่งที่ unary ในตัว+ทำคือการเปลี่ยน lvalue เป็น rvalue ตัวอย่างเช่นคุณสามารถทำได้

int x;
&x;

แต่คุณไม่สามารถทำได้

&+x;

:)

ปล. "เกินกำลัง" ไม่ใช่คำตอบที่ถูกต้องแน่นอน Unary +ได้รับการสืบทอดมาจาก C และไม่มีตัวดำเนินการระดับผู้ใช้ที่โอเวอร์โหลดใน C


14

สิ่งสำคัญที่ unary + บรรลุคือการเลื่อนประเภทเป็น int สำหรับประเภทข้อมูลที่เล็กกว่า int สิ่งนี้จะมีประโยชน์มากหากคุณพยายามพิมพ์ข้อมูลถ่านโดยใช้std::coutเป็นข้อมูลตัวเลข

char x = 5;
std::cout << +x << "\n";

แตกต่างจาก

char x=5;
std::cout << x << "\n";

นอกจากนี้ยังมีให้สำหรับการบรรทุกเกินพิกัด แต่ในทางปฏิบัติการโอเวอร์โหลดของคุณควรเกือบจะเป็น NOP


12

หากคุณจำเป็นต้องพิมพ์ค่าตัวเลขของไบต์ดิบ (เช่นตัวเลขขนาดเล็กที่จัดเก็บเป็นถ่าน) สำหรับการดีบักหรือด้วยเหตุผลใดก็ตาม unary + สามารถทำให้รหัสการพิมพ์ง่ายขึ้น พิจารณา

char c = 42;
cout << c << endl;           // prints "*\n", not what you want in this case
cout << (int)c << endl;      // prints "42\n", ok
cout << +c << endl;          // prints "42\n", much easier to type

นี่เป็นเพียงตัวอย่างสั้น ๆ ฉันแน่ใจว่ามีบางครั้งที่ unary + สามารถช่วยจัดการไบต์ของคุณให้เหมือนตัวเลขแทนการเหมือนข้อความ


4

เรื่องน่ารู้ทางประวัติศาสตร์ คณะกรรมการกำหนดมาตรฐาน C99 ยังคิดว่าการใช้ยูนารีพลัสที่มีอยู่นั้นค่อนข้างหายากโดยเห็นได้จากการพิจารณานำกลับมาใช้เพื่อให้ได้คุณสมบัติอื่นในภาษานั่นคือการยับยั้งการประเมินเวลาแปลของนิพจน์คงที่แบบทศนิยม ดูคำพูดต่อไปนี้จาก C Rationale ส่วน F.7.4:

รุ่นแรกของข้อกำหนดนี้อนุญาตให้ใช้เลขคณิตคงที่เวลาแปล แต่เพิ่มขีดความสามารถให้ตัวดำเนินการ unary + เมื่อนำไปใช้กับตัวถูกดำเนินการเพื่อยับยั้งการประเมินเวลาแปลของนิพจน์คงที่

ในท้ายที่สุดความหมายก็กลับรายการโดยมีการบังคับใช้การประเมินรันไทม์ในบริบทส่วนใหญ่ (อย่างน้อยก็ขึ้นอยู่กับกฎ "เสมือน") และความสามารถในการบังคับใช้การประเมินเวลาแปลโดยการใช้ตัวเริ่มต้นแบบคงที่ โปรดทราบว่าความแตกต่างหลักอยู่ที่การเกิดข้อยกเว้นจุดลอยตัวและการตั้งค่าการปัดเศษจุดลอยตัวอื่น ๆ ซึ่งมีอยู่


3

ไม่มาก. ข้อโต้แย้งทั่วไปสำหรับการอนุญาตให้โอเวอร์โหลดoperator+()คือมีโลกแห่งความเป็นจริงที่ใช้สำหรับการบรรทุกเกินพิกัดoperator-()และมันจะแปลกมาก (หรือไม่สมมาตร) ถ้าคุณยอมให้โอเวอร์โหลดoperator-()แต่ไม่operator+()แต่ไม่

ฉันเชื่อว่าฉันได้อ่านข้อโต้แย้งนี้จาก Stroustrop เป็นครั้งแรก แต่ฉันไม่มีหนังสือติดตัวไปด้วยเพื่อยืนยัน ฉันอาจจะผิด.


3

Unary plus มีอยู่ใน C โดยที่มันไม่ได้ทำอะไรเลย (เหมือนกับautoคีย์เวิร์ด) เพื่อที่จะไม่มีมัน Stroustrup จะต้องแนะนำความเข้ากันไม่ได้กับ C.

เมื่อมันอยู่ใน C ++ มันเป็นเรื่องธรรมดาที่จะอนุญาตให้มีฟังก์ชันโอเวอร์โหลดเช่นเดียวกับยูนารีลบและ Stroustrup อาจแนะนำด้วยเหตุผลนั้นหากยังไม่มี

ดังนั้นมันจึงไม่มีความหมาย สามารถใช้เป็นของตกแต่งเพื่อทำให้สิ่งต่างๆดูสมมาตรยิ่งขึ้นโดยใช้ +1.5 ตรงข้ามกับ -1.5 เป็นต้น ใน C ++ สามารถโอเวอร์โหลดได้ แต่จะสับสนถ้าoperator+()ทำอะไร จำกฎมาตรฐาน: เมื่อใช้ตัวดำเนินการทางคณิตศาสตร์มากเกินไปให้ทำสิ่งต่างๆเช่นintทำ

หากคุณกำลังมองหาเหตุผลว่าทำไมจึงอยู่ที่นั่นให้ค้นหาบางอย่างเกี่ยวกับประวัติในช่วงต้นของ C. ฉันสงสัยว่าไม่มีเหตุผลที่ดีเนื่องจาก C ไม่ได้ออกแบบมาจริงๆ พิจารณาautoคีย์เวิร์ดที่ไร้ประโยชน์(น่าจะตรงกันข้ามกับstaticตอนนี้ถูกรีไซเคิลใน C ++ 0x) และentryคีย์เวิร์ดซึ่งไม่เคยทำอะไรเลย (และถูกละไว้ใน C90 ในภายหลัง) มีอีเมลชื่อดังที่ Ritchie หรือ Kernighan บอกว่าเมื่อพวกเขาตระหนักว่าลำดับความสำคัญของตัวดำเนินการมีปัญหามีการติดตั้งสามครั้งพร้อมรหัสหลายพันบรรทัดที่พวกเขาไม่ต้องการทำลาย


1
การโอเวอร์โหลดที่ไม่ใช่เลขคณิตเพียงอย่างเดียวที่ฉันเห็นว่าสมเหตุสมผลคือในนิพจน์หรือไวยากรณ์ทั่วไปโดยที่ (+ term) หมายถึง tems อย่างน้อยหนึ่งตัว (ซึ่งเป็นไวยากรณ์นิพจน์ทั่วไปที่ค่อนข้างเป็นมาตรฐาน)
Michael Anderson

เกี่ยวกับentry: ( stackoverflow.com/q/254395/153285 ) ทุกวันนี้หากคุณต้องการจุดเข้าใช้งานหลายจุดเพียงแค่ใช้การโทรหางและการเพิ่มประสิทธิภาพที่แนะนำโปรไฟล์
Potatoswatter

ฉันเชื่อว่าแม้ใน C99 จะกำหนดให้extern volatile int foo;คอมไพเลอร์ที่ได้รับคำสั่ง+foo;จะต้องทำการอ่านที่อยู่ที่ระบุบนแพลตฟอร์มใด ๆ ที่อาจมีวิธีการใด ๆ เพื่อตรวจสอบว่าการอ่านเกิดขึ้น ฉันไม่แน่ใจว่าจำเป็นต้องใช้หากคำสั่งนั้นเป็นเพียง "foo" แม้ว่าคอมไพเลอร์จำนวนมากจะทำเพื่อเป็นการเอื้อเฟื้อ
supercat

2

ฉันไม่สามารถอ้างถึงแหล่งที่มาใด ๆ สำหรับสิ่งนี้ แต่ฉันเข้าใจว่ามันมีไว้สำหรับการโปรโมตประเภทที่ชัดเจนซึ่งหมายถึงการแปลงประเภทที่ไม่สูญเสีย ซึ่งวางไว้ที่ด้านบนสุดของลำดับชั้น Conversion

  • โปรโมชั่น: new_type operator+(old_type)
  • การแปลง: new_type(old_type)
  • นักแสดง: operator(new_type)(old_type)
  • การบีบบังคับ: new_type operator=(old_type)

แน่นอนว่านั่นมาจากการตีความโน้ตในคู่มือ c / c ++ ของไมโครซอฟท์ (เก่าจริงๆ) ที่ฉันอ่านเมื่อ 15 ปีที่แล้วดังนั้นจงใช้เกลือสักเม็ด


1
#include <stdio.h>
int main()
{
    unsigned short x = 5;
    printf ("%d\n",sizeof(+x)); 
    printf ("%d\n",sizeof(x)); 
    return 0;
}

ดังที่แสดงในตัวอย่างข้างต้นยูนารี + เปลี่ยนประเภทขนาด 4 และ 2 ตามลำดับ แปลกที่นิพจน์ + x คำนวณเป็นขนาดของขนาดฉันคิดว่าไม่ควร บางทีอาจเป็นเพราะ sizeof มีลำดับความสำคัญเท่ากับยูนารี +


-1

ฉันคิดว่าคุณสามารถใช้มันเพื่อสร้างตัวเลขให้เป็นบวกได้เสมอ แค่โอเวอร์โหลดตัวดำเนินการ unary + ให้เป็น abs ไม่คุ้มที่จะสร้างความสับสนให้กับนักพัฒนาเพื่อน ๆ ของคุณเว้นแต่คุณจะต้องการทำให้โค้ดของคุณสับสน จากนั้นมันจะทำงานได้ดี


นั่นจะทำให้มันไม่ตรงข้ามกับยูนารีลบซึ่งจะทำให้สับสนเว้นแต่คุณจะโอเวอร์โหลดยูนารีลบให้เป็นเอบีเอสเชิงลบซึ่งจะทำให้คณิตศาสตร์ทุกประเภทไม่ดีขึ้น
แย่ลง

เอ่อใช่ นั่นเป็นเหตุผลที่ฉันแนะนำว่าอย่าทำ
Patrick

1
@ Davy8: สิ่งที่ทำให้คุณคิดว่า unary plus คือ ในขณะนี้ "ตรงข้าม" ของ unary ลบ? ว่า "ตรงข้าม" ของผู้ประกอบการที่สมบูรณ์บิตคืออะไร~? ฉันไม่แน่ใจว่าคำจำกัดความของคุณ "ตรงข้าม" คืออะไร แต่ฉันสงสัยว่ามันมีอคติจากประสบการณ์ของสิ่งที่ยูนารีบวกและยูนารีลบทำในปัจจุบัน
ndkrempel

-1

แก้ไขเขียนซ้ำอย่างสมบูรณ์เพราะฉันรู้สึกแย่กับคำตอบเดิมของฉัน

สิ่งนี้จะช่วยให้คุณสามารถจัดการกับการประกาศประเภทของคุณอย่างชัดเจนเป็นค่าบวก (ฉันคิดว่าส่วนใหญ่ไม่ใช่การดำเนินการทางคณิตศาสตร์) ดูเหมือนว่าการปฏิเสธจะมีประโยชน์มากกว่า แต่ฉันเดาว่านี่คือตัวอย่างที่อาจสร้างความแตกต่าง:

public struct Acceleration
{
    private readonly decimal rate;
    private readonly Vector vector;

    public Acceleration(decimal rate, Vector vector)
    {
        this.vector = vector;
        this.rate = rate;
    }

    public static Acceleration operator +(Acceleration other)
    {
        if (other.Vector.Z >= 0)
        {
            return other;
        }
        return new Acceleration(other.Rate, new Vector(other.vector.X, other.Vector.Y, -other.vector.Z));
    }

    public static Acceleration operator -(Acceleration other)
    {
        if (other.Vector.Z <= 0)
        {
            return other;
        }
        return new Acceleration(other.Rate, new Vector(other.vector.X, other.Vector.Y, -other.vector.Z));
    }

    public decimal Rate
    {
        get { return rate; }
    }

    public Vector Vector
    {
        get { return vector; }
    }
}

นั่นดูไม่เหมือนการผ่าตัดสำหรับฉัน ต้องใช้สองอาร์กิวเมนต์ (เมษายนและพฤษภาคม) เพื่อให้เป็นไบนารี
BlueMonkMN

นั่นคือบวกไบนารี Unary plus เป็นเพียง "+1" หรือบางอย่างโดยไม่เหลือตัวถูกดำเนินการ
Jeffrey Hantin

ความผิดฉันเอง. ลืม CS101 ของฉัน แก้ไขแล้ว.
Michael Meadows

3
หากฉันพบว่ามีการโอเวอร์โหลดในโค้ดการผลิตฉันจะทำเครื่องหมายว่าเป็นข้อบกพร่องและแก้ไขโดยเร็วที่สุด ความหมายของ+ไม่ได้ทำให้ค่าเป็นบวก แต่จะปล่อยให้เครื่องหมายของมันไม่เปลี่ยนแปลง
Arturo Torres Sánchez

-1

เพียงแค่ใช้เพื่อโน้มน้าวใจว่าตัวเลขใดเป็นค่าบวก

เช่น;

int a=10;
System.out.println(+x);// prints 10(that means,that number 10 multiply by +1,{10*+1})

//if we use unary minus

int a=10;
System.out.println(-x);//prints -10(that means,that number 10 multiply by +1,{10*-1})
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.