เมื่อใดที่คุณควรใช้ความสามารถของ constexpr ใน C ++ 11


337

ดูเหมือนว่าสำหรับฉันแล้วการมี "ฟังก์ชั่นที่ส่งกลับค่า 5" จะทำลายหรือทำให้ความหมายของ "การเรียกใช้ฟังก์ชัน" ลดลง ต้องมีเหตุผลหรือต้องการความสามารถนี้มิเช่นนั้นจะไม่อยู่ใน C ++ 11 ทำไมถึงอยู่ที่นั่น?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

สำหรับฉันดูเหมือนว่าถ้าฉันเขียนฟังก์ชันที่ส่งคืนค่าตามตัวอักษรและฉันได้รับการตรวจสอบโค้ดใครบางคนจะบอกฉันฉันก็ควรจะประกาศค่าคงที่แทนการเขียนส่งคืน 5


28
คุณสามารถกำหนดฟังก์ชั่นวนซ้ำที่ส่งคืน a ได้constexprหรือไม่? ถ้าเป็นเช่นนั้นฉันสามารถเห็นการใช้งาน
ereOn

20
ฉันเชื่อว่าคำถามควรระบุว่า "เพราะเหตุใดจึงแนะนำคำหลักใหม่ (!) หากคอมไพเลอร์สามารถอนุมานได้ว่าตัวเองสามารถประเมินฟังก์ชันในเวลารวบรวมได้หรือไม่" การมีคำว่า "รับประกันโดยคำหลัก" นั้นฟังดูดี แต่ฉันคิดว่าฉันต้องการรับประกันคำหลักทุกครั้งที่ทำได้โดยไม่ต้องใช้คำหลัก
คอส

6
@Kos: คนที่คุ้นเคยกับ C ++ มากขึ้นอาจต้องการคำถามของคุณ แต่คำถามของฉันมาจากมุมมองของคนที่เคยเขียนรหัส C มาก่อน แต่ไม่คุ้นเคยกับคำหลัก C ++ 2011 เลยหรือรายละเอียดการใช้งาน C ++ . ความสามารถในการให้เหตุผลเกี่ยวกับการปรับให้เหมาะสมของคอมไพเลอร์และการลดค่าคงที่ของนิพจน์เป็นหัวข้อสำหรับคำถามผู้ใช้ขั้นสูงมากกว่าคำถามนี้
Warren P

8
@ คอสฉันคิดตามบรรทัดเดียวกับคุณและคำตอบที่ฉันพบคือโดยไม่มี constexpr คุณจะรู้ได้อย่างไรว่าคอมไพเลอร์รวบรวมเวลาประเมินฟังก์ชั่นสำหรับคุณหรือไม่ ฉันคิดว่าคุณสามารถตรวจสอบเอาต์พุตของแอสเซมบลีเพื่อดูว่ามันทำอะไรได้บ้าง แต่มันง่ายกว่าที่จะบอกคอมไพเลอร์ว่าคุณต้องการการปรับให้เหมาะสมที่สุดและถ้าด้วยเหตุผลบางอย่างมันทำไม่ได้สำหรับคุณ ข้อผิดพลาดแทนที่จะล้มเหลวในการเพิ่มประสิทธิภาพที่คุณคาดว่าจะเพิ่มประสิทธิภาพอย่างเงียบ ๆ
Jeremy Friesner

3
@Kos: constคุณสามารถพูดในสิ่งเดียวกันเกี่ยวกับ ในความเป็นจริงความตั้งใจที่ได้รับคำสั่งมีประโยชน์ ! ขนาด Array เป็นตัวอย่างที่ยอมรับได้
การแข่งขัน Lightness ใน Orbit

คำตอบ:


303

สมมติว่ามันทำอะไรที่ซับซ้อนไปหน่อย

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

ตอนนี้คุณมีบางสิ่งที่สามารถประเมินได้จนถึงค่าคงที่ในขณะที่ยังคงความสามารถในการอ่านได้ดีและให้การประมวลผลที่ซับซ้อนเล็กน้อยกว่าเพียงแค่ตั้งค่าคงที่เป็นตัวเลข

โดยทั่วไปแล้วจะให้ความช่วยเหลือที่ดีในการบำรุงรักษาเมื่อเห็นได้ชัดว่าคุณกำลังทำอะไรอยู่ ใช้max( a, b )ตัวอย่างเช่น:

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

มันเป็นตัวเลือกที่เรียบง่าย แต่ก็มีความหมายว่าถ้าคุณโทรmaxด้วยค่าคงที่มันจะถูกคำนวณอย่างชัดเจนในเวลารวบรวมและไม่ใช่เวลาทำงาน

อีกตัวอย่างที่ดีคือDegreesToRadiansฟังก์ชั่น ทุกคนพบว่าองศาอ่านง่ายกว่าเรเดียน ในขณะที่คุณอาจรู้ว่า 180 องศาเป็นเรเดียนมันชัดเจนกว่ามากเขียนดังนี้:

const float oneeighty = DegreesToRadians( 180.0f );

ข้อมูลที่ดีมากมายที่นี่:

http://en.cppreference.com/w/cpp/language/constexpr


18
มีจุดที่ยอดเยี่ยมในการบอกคอมไพเลอร์ให้ลองและคำนวณค่า ณ เวลารวบรวม ฉันสงสัยว่าทำไม const ไม่ให้ฟังก์ชันนี้เมื่อมีการระบุการเพิ่มประสิทธิภาพเฉพาะ หรือไม่
TamusJRoyce

11
@Tamus: บ่อยครั้งที่มันจะ แต่มันไม่จำเป็นต้อง constexpr บังคับให้คอมไพเลอร์และจะคายข้อผิดพลาดออกหากไม่สามารถทำได้
Goz

20
ฉันเห็นมันตอนนี้ บาป (0.5) เป็นอีกเรื่องหนึ่ง สิ่งนี้แทนที่มาโคร C อย่างเรียบร้อย
Warren P

10
ฉันเห็นว่านี่เป็นคำถามสัมภาษณ์ใหม่: อธิบายความแตกต่างระหว่างคำหลัก const และ constexpr
Warren P

2
เป็นวิธีการบันทึกจุดนี้สำหรับตัวเองฉันเขียนรหัสที่คล้ายกันดังกล่าวข้างต้นและอีกครั้งด้วยฟังก์ชั่นเป็น "const" มากกว่า "constexpr" ขณะที่ฉันใช้ Clang3.3, -pedantic-errors และ -std = c ++ 11 ฉันคาดว่าอันหลังจะไม่คอมไพล์ มันรวบรวมและวิ่งตามในกรณี "constexpr" คุณคิดว่านี่เป็นส่วนขยายเสียงดังกราวหรือมีการปรับแต่งข้อมูลจำเพาะ C ++ 11 ตั้งแต่ตอบกระทู้นี้หรือไม่?
Arbalest

144

บทนำ

constexprไม่ได้ถูกนำมาใช้เป็นวิธีที่จะบอกการดำเนินการว่าบางสิ่งสามารถประเมินได้ในบริบทที่ต้องมีการแสดงออกอย่างต่อเนื่อง ; การใช้งานที่สอดคล้องกันสามารถพิสูจน์สิ่งนี้ได้ก่อน C ++ 11

สิ่งที่การนำไปปฏิบัติไม่สามารถพิสูจน์ได้คือเจตนาของรหัสบางส่วน:

  • มันคืออะไรที่นักพัฒนาต้องการที่จะแสดงกับเอนทิตีนี้?
  • เราควรอนุญาตให้ใช้รหัสในการแสดงออกอย่างต่อเนื่องแบบสุ่มสี่สุ่มห้าเพียงเพราะมันเกิดขึ้นในการทำงานหรือไม่

โลกจะปราศจากconstexprอะไร

สมมติว่าคุณกำลังพัฒนาไลบรารีและตระหนักว่าคุณต้องการคำนวณผลรวมของจำนวนเต็มทุกตัวในช่วงเวลา(0,N]นั้น

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

การขาดความตั้งใจ

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

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

T arr[f(10)]; // freakin' magic

การเพิ่มประสิทธิภาพ

คุณเป็น"น่ากลัว"นักพัฒนาห้องสมุดตัดสินใจว่าfควรแคชผลเมื่อมีการเรียก; ใครจะต้องการคำนวณค่าชุดเดียวกันซ้ำไปซ้ำมา

int func (int n) { 
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ()) 
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

ผลลัพธ์

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

คุณไม่เคยสัญญาว่าฟังก์ชั่นนั้นสามารถใช้งานได้อย่างต่อเนื่องและถ้าไม่มีconstexprก็จะไม่มีวิธีให้สัญญาเช่นนั้น


ดังนั้นทำไมเราต้องconstexpr?

การใช้งานหลักของconstexprคือการประกาศเจตนา

หากเอนทิตีไม่ได้ถูกทำเครื่องหมายว่าเป็นconstexpr- มันไม่เคยตั้งใจที่จะใช้ในการแสดงออกคงที่ ; และแม้ว่าจะเป็นไปได้ก็ตามเราพึ่งพาคอมไพเลอร์เพื่อวิเคราะห์บริบทดังกล่าว (เพราะไม่สนใจเจตนาของเรา)


25
นี้น่าจะเป็นคำตอบที่ถูกต้องเนื่องจากการเปลี่ยนแปลงล่าสุดใน C ++ 14 และ C ++ 17 ช่วยให้ช่วงกว้างมากของภาษาที่จะใช้ในconstexprการแสดงออก กล่าวอีกนัยหนึ่งมีอะไรที่สามารถทำหมายเหตุประกอบได้บ้างconstexpr (บางทีวันหนึ่งมันก็จะหายไปเพราะเรื่องนี้?) และถ้าไม่มีเกณฑ์ในการใช้constexprหรือไม่ควรเขียนโค้ดทั้งหมดเช่นนั้น .
alecov

4
@alecov Definitly ไม่ได้ทุกอย่าง ... I/O, syscallและdynamic memory allocationแน่นอน cann't ถูกทำเครื่องหมายเป็นconstexprนอกจากนี้ทุกอย่างไม่ควรconstexprจะเป็น
JiaHao Xu

1
@alecov ฟังก์ชั่นบางอย่างตั้งใจที่จะถูกดำเนินการในขณะทำงานและไม่มีความหมายที่จะทำสิ่งนี้ในเวลารวบรวม
JiaHao Xu

1
ฉันชอบคำตอบนี้ดีที่สุด การประเมินเวลาคอมไพล์คือการปรับให้เหมาะสมที่สุด แต่สิ่งที่คุณได้รับจากconstexprการรับประกันของพฤติกรรมบางอย่าง เช่นเดียวกับที่constทำ
Tomáš Zato - Reinstate Monica

คอมไพเลอร์ตัวไหนที่ทำให้int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)]; ฉันไม่สามารถรวบรวมเวอร์ชั่น นี้ได้
Jer

91

ใช้std::numeric_limits<T>::max(): ด้วยเหตุผลใดก็ตามนี่เป็นวิธี constexprจะเป็นประโยชน์ที่นี่

อีกตัวอย่าง: คุณต้องการประกาศ C-array (หรือ a std::array) ที่มีขนาดใหญ่เท่ากับอาร์เรย์อื่น วิธีการทำเช่นนี้ในขณะนี้เป็นเช่น:

int x[10];
int y[sizeof x / sizeof x[0]];

แต่จะดีกว่าไหมถ้าจะเขียน:

int y[size_of(x)];

ขอบคุณconstexprคุณสามารถ:

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}

1
+1 สำหรับการใช้เทมเพลตที่ดี แต่มันจะทำงานเหมือนกันทุกอย่างโดยไม่มี constexpr ใช่ไหม
คอส

21
@Kos: ไม่มันจะคืนค่ารันไทม์ constexprบังคับให้ผู้รวบรวมเพื่อให้ฟังก์ชันส่งคืนค่าเวลาคอมไพล์ (ถ้าทำได้)
deft_code

14
@Kos: หากไม่มีconstexprจะไม่สามารถใช้ในการประกาศขนาดอาร์เรย์หรือเป็นอาร์กิวเมนต์เทมเพลตไม่ว่าผลลัพธ์ของการเรียกฟังก์ชันจะเป็นค่าคงที่เวลาการคอมไพล์หรือไม่ก็ตาม โดยพื้นฐานแล้วทั้งสองกรณีนี้เป็นเพียงกรณีใช้งานconstexprแต่อย่างน้อยกรณีอาร์กิวเมนต์แม่แบบการใช้ก็มีความสำคัญ
Konrad Rudolph

2
"ไม่ว่าด้วยเหตุผลใดก็ตามนี่เป็นวิธีการ": เหตุผลก็คือมีเพียงจำนวนเต็มเวลาในการรวบรวม C ++ 03 แต่ไม่มีประเภทเวลาการคอมไพล์อื่น ๆ ดังนั้นวิธีการเดียวที่สามารถทำงานกับประเภทใดก็ได้ก่อน C ++ 11
เซบาสเตียนมัค

5
@LwCui ไม่ไม่ใช่ "ok": GCC เป็นเพียงค่าเริ่มต้นเกี่ยวกับบางสิ่ง ใช้-pedanticตัวเลือกและมันจะถูกตั้งค่าสถานะเป็นข้อผิดพลาด
Konrad Rudolph

19

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

อย่างไรก็ตามการใช้งานอย่างใดอย่างหนึ่งของconstexprไม่มีค่าคงตัวพิมพ์ C ++ 03

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;

12
คุณช่วยอธิบายได้ไหมว่า "ข้อผิดพลาดตัวเชื่อมโยงที่ลึกซึ้งมาก" หรืออย่างน้อยก็ให้ตัวชี้ไปยังการชี้แจง?
enobayram

4
@enobayram ผู้ประกอบการที่ประกอบไปด้วยที่อยู่ของตัวถูกดำเนินการ ที่ไม่ชัดเจนจากรหัส ทุกอย่างรวบรวมได้ดี แต่ลิงก์ล้มเหลวเนื่องจากที่อยู่fourไม่สามารถแก้ไข ฉันต้องขุดเพื่อหาว่าใครกำลังพูดถึงstatic constตัวแปรของฉัน
deft_code

23
"สิ่งนี้ไม่ดีสำหรับเหตุผลที่ชัดเจน": เหตุผลที่ชัดเจนที่สุดคือการใช้เครื่องหมายอัฒภาคใช่ไหม?
TonyK

4
"ข้อผิดพลาดตัวเชื่อมโยงที่ลึกซึ้งมาก" ทำให้ฉันงงงวยอย่างสมบูรณ์ ทั้งfourมิได้fiveอยู่ในขอบเขต
Steven Lu

3
ดูenum classประเภทใหม่มันจะแก้ไขปัญหา enum บางอย่าง
ninMonkey

14

จากสิ่งที่ฉันได้อ่านความจำเป็นในการใช้ constexpr มาจากปัญหาใน metaprogramming คลาสลักษณะอาจมีค่าคงที่ที่แสดงเป็นฟังก์ชันให้คิดว่า numeric_limits :: max () ด้วย constexpr ฟังก์ชันประเภทนั้นสามารถใช้ใน metaprogramming หรือเป็น array array ฯลฯ เป็นต้น

อีกตัวอย่างหนึ่งจากด้านบนของหัวของฉันจะเป็นสำหรับอินเทอร์เฟซสำหรับคลาสคุณอาจต้องการชนิดที่ได้รับกำหนดค่าคงที่ของตนเองสำหรับการดำเนินการบางอย่าง

แก้ไข:

หลังจาก poking รอบในดังนั้นดูเหมือนว่าคนอื่น ๆ ได้เกิดขึ้นกับบาง ตัวอย่างของสิ่งที่อาจจะเป็นไปได้ด้วย constexprs


"หากต้องการเป็นส่วนหนึ่งของอินเทอร์เฟซคุณต้องเป็นฟังก์ชัน"
Daniel Earwicker

ตอนนี้ฉันเห็นประโยชน์ของสิ่งนี้แล้วฉันก็รู้สึกตื่นเต้นเกี่ยวกับ C ++ 0x อีกเล็กน้อย ดูเหมือนจะเป็นสิ่งที่คิดออกมาดี ฉันรู้ว่าพวกเขาจะต้องเป็น ภาษามาตรฐาน uber-geeks ไม่ค่อยทำแบบสุ่ม
Warren P

ฉันรู้สึกตื่นเต้นมากขึ้นเกี่ยวกับ lambdas, รูปแบบการทำเกลียว, initializer_list, การอ้างอิง rvalue, เทมเพลต variadic, การผูกโอเวอร์โหลดใหม่ ...
ลุค

1
ใช่แล้ว แต่ฉันเข้าใจแลมบ์ดา / การปิดใน languges อื่น ๆ แล้ว constexprมีประโยชน์มากกว่าโดยเฉพาะในคอมไพเลอร์ที่มีระบบประเมินผลนิพจน์เวลารวบรวมที่มีประสิทธิภาพ C ++ ไม่มีเพื่อนในโดเมนนั้นจริงๆ (นั่นเป็นคำชมที่ยอดเยี่ยมสำหรับ C ++ 11, IMHO)
Warren P

11

จากคำพูดของ Stroustrup ที่ "Going Native 2012":

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s’
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2’ 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human

2
ตัวอย่างนี้ยังสามารถพบได้ในกระดาษ Stroustrup ของการพัฒนาซอฟแวร์สำหรับโครงสร้างพื้นฐาน
Matthieu Poullet

clang-3.3: ข้อผิดพลาด: ชนิดส่งคืนของฟังก์ชัน constexpr 'Value <Second>' ไม่ใช่ประเภทตามตัวอักษร
Mitja

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

5
@bobobobo หรือถ้าคุณกำลังเขียนซอฟแวร์นำทางสำหรับดาวอังคารยานอวกาศสภาพภูมิอากาศอาจ :)
Jeremy Friesner

1
ในการทำให้คอมไพล์ - 1 ใช้เครื่องหมายขีดล่างในคำต่อท้ายตามตัวอักษร 2. เพิ่มโอเปอเรเตอร์ "" _m สำหรับ 100_m 3. ใช้ 100.0_m หรือเพิ่มโอเวอร์โหลดที่ยอมรับความยาวที่ไม่ได้ลงชื่อ 4. ประกาศตัวสร้างค่า constexpr 5. เพิ่มโอเปอเรเตอร์ที่เกี่ยวข้อง / ลงในคลาส Value ดังนี้: constexpr auto operator / (ค่า const <Y> และอื่น ๆ ) const {คืนค่า <หน่วย <TheUnit :: m - ค่า <Y> :: TheUnit :: m, TheUnit :: kg - Value <Y> :: TheUnit :: kg, TheUnit :: s - Value <Y> :: TheUnit :: s >> (val / other.val); } โดยที่ TheUnit พิมพ์ typedef สำหรับหน่วยที่เพิ่มเข้าไปในคลาส Value
0kcats

8

การใช้งานอื่น (ยังไม่ได้กล่าวถึง) คือสิ่งconstexprก่อสร้าง สิ่งนี้ช่วยให้การสร้างค่าคงที่เวลาคอมไพล์ซึ่งไม่จำเป็นต้องเริ่มต้นในระหว่างรันไทม์

const std::complex<double> meaning_of_imagination(0, 42); 

จับคู่กับตัวอักษรที่กำหนดโดยผู้ใช้และคุณได้รับการสนับสนุนอย่างเต็มที่สำหรับคลาสที่ผู้ใช้กำหนดตามตัวอักษร

3.14D + 42_i;

6

เคยมีรูปแบบด้วยโปรแกรมเมตาโปรแกรม:

template<unsigned T>
struct Fact {
    enum Enum {
        VALUE = Fact<T-1>*T;
    };
};

template<>
struct Fact<1u> {
    enum Enum {
        VALUE = 1;
    };
};

// Fact<10>::VALUE is known be a compile-time constant

ฉันเชื่อว่าconstexprมีการนำมาใช้เพื่อให้คุณเขียนโครงสร้างดังกล่าวโดยไม่ต้องใช้แม่แบบและโครงสร้างแปลก ๆ ด้วยความเชี่ยวชาญ, SFINAE และเนื้อหา - แต่เหมือนกับที่คุณเขียนฟังก์ชันเวลาทำงาน แต่รับประกันได้ว่าผลลัพธ์จะถูกรวบรวม -เวลา.

อย่างไรก็ตามโปรดทราบว่า:

int fact(unsigned n) {
    if (n==1) return 1;
    return fact(n-1)*n;
}

int main() {
    return fact(10);
}

รวบรวมสิ่งนี้ด้วยg++ -O3และคุณจะเห็นว่าfact(10)มีการถอนตัวในเวลารวบรวม!

คอมไพเลอร์ตระหนักถึง VLA (ดังนั้นคอมไพเลอร์ C ในโหมด C99 หรือคอมไพเลอร์ C ++ ที่มีนามสกุล C99) อาจอนุญาตให้คุณทำ:

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

แต่ในขณะนี้มันยังไม่ได้มาตรฐาน C ++ - constexprดูเหมือนว่าจะมีวิธีต่อสู้กับสิ่งนี้ (แม้จะไม่มี VLA ในกรณีข้างต้น) และยังคงมีปัญหาที่ต้องมีนิพจน์คงที่ "เป็นทางการ" เป็นอาร์กิวเมนต์เทมเพลต


ฟังก์ชั่นความเป็นจริงไม่ได้รับการประเมินที่รวบรวมเวลา มันต้องเป็น constexpr และจะต้องมีคำสั่ง return เพียงคำสั่งเดียว
Sumant

1
@Sumant: คุณถูกต้องที่ไม่ต้องมีการประเมินเวลารวบรวม แต่มันเป็น! ฉันอ้างถึงสิ่งที่เกิดขึ้นจริงในคอมไพเลอร์ รวบรวมไว้ใน GCC เมื่อเร็ว ๆ นี้ดูผล asm และตรวจสอบด้วยตัวคุณเองถ้าคุณไม่เชื่อฉัน!
Kos

ลองเพิ่มstd::array<int, fact(2)>แล้วคุณจะเห็นว่า fact () ไม่ได้รับการประเมิน ณ เวลารวบรวม เป็นเพียงเครื่องมือเพิ่มประสิทธิภาพ GCC ที่ทำงานได้ดี

1
นั่นคือสิ่งที่ฉันพูด ... ฉันไม่แน่ใจจริงๆเหรอ? ดูย่อหน้าสุดท้าย
Kos

5

เพิ่งเริ่มเปลี่ยนโครงการเป็น c ++ 11 และพบกับสถานการณ์ที่ดีอย่างสมบูรณ์สำหรับ constexpr ซึ่งจะทำความสะอาดวิธีการอื่นในการดำเนินการเดียวกัน จุดสำคัญที่นี่คือที่คุณสามารถวางฟังก์ชั่นในการประกาศขนาดอาร์เรย์เมื่อมีการประกาศ constexpr มีหลายสถานการณ์ที่ฉันเห็นว่านี่เป็นประโยชน์อย่างมากในการก้าวไปข้างหน้าด้วยส่วนของรหัสที่ฉันเกี่ยวข้อง

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}

4
สิ่งนี้สามารถเขียนได้อย่างเท่าเทียมกัน: const size_t MaxIPV4StringLength = sizeof ("255.255.255.255");
Superfly Jon

static inline constexpr const autoอาจจะดีกว่า
JiaHao Xu

3

คำตอบอื่น ๆ ทั้งหมดนั้นยอดเยี่ยมฉันแค่อยากจะยกตัวอย่างของสิ่งหนึ่งที่คุณสามารถทำได้ด้วย constexpr ที่น่าทึ่ง See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) เป็นตัวแยกวิเคราะห์เวลา HTML และเครื่องมือเทมเพลต ซึ่งหมายความว่าคุณสามารถใส่ HTML และนำทรีที่สามารถจัดการได้ เมื่อการแยกวิเคราะห์เสร็จสิ้นในเวลารวบรวมจะทำให้คุณมีประสิทธิภาพเพิ่มขึ้นเล็กน้อย

จากตัวอย่างหน้า GitHub:

#include <iostream>
#include "seephit.h"
using namespace std;



int main()
{
  constexpr auto parser =
    R"*(
    <span >
    <p  color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p  >
    </span>
    )*"_html;

  spt::tree spt_tree(parser);

  spt::template_dict dct;
  dct["name"] = "Mary";
  dct["profession"] = "doctor";
  dct["city"] = "London";

  spt_tree.root.render(cerr, dct);
  cerr << endl;

  dct["city"] = "New York";
  dct["name"] = "John";
  dct["profession"] = "janitor";

  spt_tree.root.render(cerr, dct);
  cerr << endl;
}

1

ตัวอย่างพื้นฐานของคุณทำหน้าที่เขาโต้แย้งเช่นเดียวกับค่าคงที่ตัวเอง ทำไมต้องใช้

static const int x = 5;
int arr[x];

เกิน

int arr[5];

เพราะมันเป็นวิธีการบำรุงรักษามากขึ้น การใช้ constexpr นั้นเร็วกว่ามากในการเขียนและอ่านมากกว่าเทคนิค metaprogramming ที่มีอยู่


0

มันสามารถเปิดใช้งานการเพิ่มประสิทธิภาพใหม่บางอย่าง constตามเนื้อผ้าเป็นคำใบ้สำหรับระบบประเภทและไม่สามารถใช้สำหรับการปรับให้เหมาะสม (เช่นconstฟังก์ชั่นสมาชิกสามารถconst_castและปรับเปลี่ยนวัตถุอย่างไรก็ตามตามกฎหมายดังนั้นจึงconstไม่น่าเชื่อถือสำหรับการปรับให้เหมาะสม)

constexprหมายถึงการแสดงออกจริงๆคงที่ให้ปัจจัยการผลิตเพื่อฟังก์ชั่นที่มี const พิจารณา:

class MyInterface {
public:
    int GetNumber() const = 0;
};

หากสิ่งนี้ถูกเปิดเผยในโมดูลอื่น ๆ คอมไพเลอร์ไม่สามารถไว้วางใจได้ว่าGetNumber()จะไม่ส่งคืนค่าที่แตกต่างกันในแต่ละครั้งที่ถูกเรียกใช้ - แม้ต่อเนื่องโดยไม่มีการโทรที่ไม่ใช่ const ระหว่างนั้นเพราะconstอาจถูกทิ้งในการนำไปใช้ (เห็นได้ชัดว่าโปรแกรมเมอร์คนใดที่ทำสิ่งนี้ควรถูกยิง แต่ภาษาอนุญาตมันดังนั้นผู้แปลจึงต้องปฏิบัติตามกฎ)

การเพิ่มconstexpr:

class MyInterface {
public:
    constexpr int GetNumber() const = 0;
};

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


ที่จริงconst สามารถใช้ในการปรับให้เหมาะสม ... มันเป็นพฤติกรรมที่ไม่ได้กำหนดเพื่อแก้ไขค่าที่กำหนด constแม้หลังจากconst_castIIRC ฉันคาดว่ามันจะสอดคล้องกับconstฟังก์ชั่นสมาชิก แต่ฉันต้องตรวจสอบกับมาตรฐาน นี่หมายความว่าคอมไพเลอร์สามารถทำการปรับให้เหมาะสมได้อย่างปลอดภัย
คอส

1
@Warren: ไม่สำคัญว่าการเพิ่มประสิทธิภาพเสร็จสิ้นจริง ๆ แล้วอนุญาตเพียง @ คอส: มันเป็นความฉลาดเล็กน้อยที่รู้ว่าถ้าวัตถุดั้งเดิมไม่ได้ประกาศ const ( int xเทียบกับconst int x) ดังนั้นมันจะปลอดภัยในการปรับเปลี่ยนโดยการconst_castกำจัด const บนตัวชี้ / การอ้างอิง มิฉะนั้นconst_castจะเรียกใช้พฤติกรรมที่ไม่ได้กำหนดเสมอและไร้ประโยชน์ :) ในกรณีนี้คอมไพเลอร์ไม่มีข้อมูลเกี่ยวกับ const-ness ของวัตถุดั้งเดิมดังนั้นจึงไม่สามารถบอกได้
AshleysBrain

@Kos ฉันไม่คิดว่า const_cast เป็นปัญหาเดียวที่นี่ วิธีการ const ได้รับอนุญาตให้อ่านและปรับเปลี่ยนตัวแปรทั่วโลก ในทางกลับกันบางคนจากเธรด anpther ยังสามารถปรับเปลี่ยนวัตถุ const ระหว่างการโทร
enobayram

1
"= 0" ไม่ถูกต้องที่นี่และควรถูกลบออก ฉันจะทำเอง แต่ฉันไม่แน่ใจว่าเป็นไปตามโปรโตคอล SO
KnowItAllWannabe

ตัวอย่างทั้งสองไม่ถูกต้อง: อันแรก ( int GetNumber() const = 0;) ควรประกาศGetNumber()เมธอดเสมือน ที่สอง ( constexpr int GetNumber() const = 0;) ไม่ถูกต้องเพราะตัวระบุบริสุทธิ์ ( = 0) หมายถึงวิธีการที่จะเป็นเสมือน แต่ constexpr ต้องไม่เป็นเสมือน (อ้างอิง: en.cppreference.com/w/cpp/language/constexpr )
stj

-1

ควรใช้เมื่อใดconstexpr:

  1. เมื่อใดก็ตามที่มีการรวบรวมเวลาคงที่

ในขณะที่ผมเห็นด้วยกับคุณคำตอบนี้ไม่ได้อธิบายว่าทำไม constexprควรจะต้องการมากกว่าแมโคร preprocessor constหรือ
Sneftel

-3

มันมีประโยชน์สำหรับบางสิ่งเช่น

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

int some_arr[MeaningOfLife()];

ผูกนี้กับชั้นเรียนลักษณะหรือชอบและมันจะมีประโยชน์มาก


4
ในตัวอย่างของคุณมีข้อได้เปรียบมากกว่าค่าคงที่ธรรมดาดังนั้นจึงไม่ตอบคำถาม
jalf

นี่เป็นตัวอย่างที่วางแผนไว้ลองนึกภาพว่า MeaningOfLife () ได้รับคุณค่าจากที่อื่นให้พูดอีกฟังก์ชันหนึ่งหรือ #define หรืออนุกรมดังกล่าว คุณอาจไม่รู้ว่ามันส่งคืนอะไรอาจเป็นรหัสห้องสมุด ตัวอย่างอื่น ๆ ลองนึกภาพคอนเทนเนอร์ที่ไม่เปลี่ยนรูปซึ่งมีขนาด constexpr () ตอนนี้คุณสามารถทำ int arr [container.size ()];
อ่าน

2
@plivesey คุณสามารถแก้ไขคำตอบของคุณด้วยตัวอย่างที่ดีกว่าได้แล้ว
Mukesh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.