เป็นไปได้ไหมที่จะเขียนข้อความยืนยันมากเกินไป?
แน่นอนมันเป็น [ลองนึกภาพตัวอย่างที่น่ารังเกียจที่นี่] อย่างไรก็ตามการใช้แนวทางที่มีรายละเอียดดังต่อไปนี้คุณไม่ควรมีปัญหาในการผลักดันข้อ จำกัด ดังกล่าวในทางปฏิบัติ ฉันเป็นแฟนตัวยงของการยืนยันเช่นกันและฉันใช้พวกเขาตามหลักการเหล่านี้ คำแนะนำนี้ส่วนใหญ่ไม่ได้เป็นพิเศษในการยืนยัน แต่เฉพาะการปฏิบัติทางวิศวกรรมที่ดีทั่วไปที่ใช้กับพวกเขา
คำนึงถึงค่าใช้จ่ายในการดำเนินการและฐานรากไบนารี
การยืนยันเป็นสิ่งที่ดี แต่หากพวกเขาทำให้โปรแกรมของคุณช้าจนไม่อาจยอมรับได้มันจะน่ารำคาญอย่างยิ่งหรือคุณจะปิดมันไม่ช้าก็เร็ว
ฉันชอบที่จะวัดค่าใช้จ่ายของการยืนยันที่เกี่ยวข้องกับค่าใช้จ่ายของฟังก์ชั่นที่มีอยู่ในพิจารณาสองตัวอย่างต่อไปนี้
// Precondition: queue is not empty
// Invariant: queue is sorted
template <typename T>
const T&
sorted_queue<T>::max() const noexcept
{
assert(!this->data_.empty());
assert(std::is_sorted(std::cbegin(this->data_), std::cend(this->data_)));
return this->data_.back();
}
ฟังก์ชั่นของตัวเองคือการดำเนินการO (1) แต่การยืนยันบัญชีสำหรับค่าใช้จ่ายO ( n ) ฉันไม่คิดว่าคุณต้องการให้เช็คดังกล่าวใช้งานได้เว้นแต่ในกรณีพิเศษ
นี่คือฟังก์ชั่นอื่นที่มีการยืนยันที่คล้ายกัน
// Requirement: op : T -> T is monotonic [ie x <= y implies op(x) <= op(y)]
// Invariant: queue is sorted
// Postcondition: each item x in the queue is replaced by op(x)
template <typename T>
template <typename FuncT>
void
sorted_queue<T>::apply_monotonic_function(FuncT&& op)
{
assert(std::is_sorted(std::cbegin(this->data_), std::cend(this->data_)));
std::transform(std::cbegin(this->data_), std::cend(this->data_),
std::begin(this->data_), std::forward<FuncT>(op));
assert(std::is_sorted(std::cbegin(this->data_), std::cend(this->data_)));
}
ฟังก์ชั่นของตัวเองคือการดำเนินการO ( n ) ดังนั้นจึงเจ็บน้อยลงเพื่อเพิ่มค่าใช้จ่ายO ( n ) เพิ่มเติมสำหรับการยืนยัน การทำให้ฟังก์ชั่นช้าลงโดยปัจจัยขนาดเล็ก (ในกรณีนี้อาจน้อยกว่า 3) เป็นสิ่งที่เรามักจะสามารถจ่ายได้ในบิลด์ debug แต่อาจไม่ได้อยู่ใน build build
ลองพิจารณาตัวอย่างนี้
// Precondition: queue is not empty
// Invariant: queue is sorted
// Postcondition: last element is removed from queue
template <typename T>
void
sorted_queue<T>::pop_back() noexcept
{
assert(!this->data_.empty());
return this->data_.pop_back();
}
ในขณะที่หลายคนอาจจะพอใจกับการยืนยันO (1) นี้มากกว่าการยืนยันO ( n ) สองรายการในตัวอย่างก่อนหน้าพวกเขามีความเท่าเทียมทางศีลธรรมในมุมมองของฉัน แต่ละค่าใช้จ่ายเพิ่มตามลำดับความซับซ้อนของฟังก์ชันเอง
ในที่สุดก็มีการยืนยันที่ "ถูกจริงๆ" ซึ่งถูกครอบงำด้วยความซับซ้อนของฟังก์ชั่นที่มีอยู่
// Requirement: cmp : T x T -> bool is a strict weak ordering
// Precondition: queue is not empty
// Postcondition: if x is returned, then there is no y in the queue
// such that cmp(x, y)
template <typename T>
template <typename CmpT>
const T&
sorted_queue<T>::max(CmpT&& cmp) const
{
assert(!this->data_.empty());
const auto pos = std::max_element(std::cbegin(this->data_),
std::cend(this->data_),
std::forward<CmpT>(cmp));
assert(pos != std::cend(this->data_));
return *pos;
}
ที่นี่เรามีการยืนยันสองO (1) ในฟังก์ชั่นO ( n ) มันอาจจะไม่เป็นปัญหาในการรักษาค่าใช้จ่ายนี้แม้จะอยู่ในรุ่นที่วางจำหน่าย
อย่างไรก็ตามโปรดจำไว้ว่าความซับซ้อนเชิงซีมโทติคนั้นไม่ได้ให้การประเมินที่เพียงพอเสมอไปเพราะในทางปฏิบัติเรามักจะจัดการกับขนาดอินพุตที่ล้อมรอบด้วยปัจจัยคงที่แน่นอนและค่าคงที่ที่ซ่อนอยู่โดย "บิ๊ก - โอ "
ดังนั้นตอนนี้เราได้ระบุสถานการณ์ที่แตกต่างกันแล้วเราจะทำอะไรได้บ้าง วิธีง่าย ๆ (น่าจะเกินไป) ก็คือการทำตามกฎเช่น“ อย่าใช้คำยืนยันที่ควบคุมฟังก์ชั่นที่มีอยู่” ในขณะที่มันอาจใช้ได้กับบางโครงการ สิ่งนี้สามารถทำได้โดยใช้มาโครการยืนยันที่แตกต่างกันสำหรับเคสที่แตกต่างกัน
#define MY_ASSERT_IMPL(COST, CONDITION) \
( \
( ((COST) <= (MY_ASSERT_COST_LIMIT)) && !(CONDITION) ) \
? ::my::assertion_failed(__FILE__, __LINE__, __FUNCTION__, # CONDITION) \
: (void) 0 \
)
#define MY_ASSERT_LOW(CONDITION) \
MY_ASSERT_IMPL(MY_ASSERT_COST_LOW, CONDITION)
#define MY_ASSERT_MEDIUM(CONDITION) \
MY_ASSERT_IMPL(MY_ASSERT_COST_MEDIUM, CONDITION)
#define MY_ASSERT_HIGH(CONDITION) \
MY_ASSERT_IMPL(MY_ASSERT_COST_HIGH, CONDITION)
#define MY_ASSERT_COST_NONE 0
#define MY_ASSERT_COST_LOW 1
#define MY_ASSERT_COST_MEDIUM 2
#define MY_ASSERT_COST_HIGH 3
#define MY_ASSERT_COST_ALL 10
#ifndef MY_ASSERT_COST_LIMIT
# define MY_ASSERT_COST_LIMIT MY_ASSERT_COST_MEDIUM
#endif
namespace my
{
[[noreturn]] extern void
assertion_failed(const char * filename, int line, const char * function,
const char * message) noexcept;
}
ตอนนี้คุณสามารถใช้สามแมโครMY_ASSERT_LOW
, MY_ASSERT_MEDIUM
และMY_ASSERT_HIGH
แทนของไลบรารีมาตรฐานของ“หนึ่งขนาดเหมาะกับทุกคน” assert
แมโครสำหรับยืนยันที่ได้รับการครอบงำโดยไม่ถูกครอบงำโดยมิได้มีอำนาจเหนือและมีอำนาจเหนือความซับซ้อนของฟังก์ชั่นที่มีของพวกเขาตามลำดับ เมื่อคุณสร้างซอฟต์แวร์คุณสามารถกำหนดสัญลักษณ์ตัวประมวลผลล่วงหน้าล่วงหน้าMY_ASSERT_COST_LIMIT
เพื่อเลือกชนิดของการยืนยันที่ควรทำให้เป็นสิ่งที่เรียกใช้งานได้ ค่าคงที่MY_ASSERT_COST_NONE
และMY_ASSERT_COST_ALL
ไม่สอดคล้องกับแมโครยืนยันใด ๆ และมีไว้เพื่อใช้เป็นค่าสำหรับMY_ASSERT_COST_LIMIT
เพื่อปิดการยืนยันทั้งหมดหรือเปิดตามลำดับ
เราพึ่งพาสมมติฐานที่นี่ว่าคอมไพเลอร์ที่ดีจะไม่สร้างรหัสใด ๆ
if (false_constant_expression && run_time_expression) { /* ... */ }
และแปลง
if (true_constant_expression && run_time_expression) { /* ... */ }
เข้าไป
if (run_time_expression) { /* ... */ }
ซึ่งฉันเชื่อว่าเป็นสมมติฐานที่ปลอดภัยในปัจจุบัน
หากคุณกำลังจะปรับแต่งโค้ดข้างต้นพิจารณาคำอธิบายประกอบคอมไพเลอร์ที่เฉพาะเจาะจงเช่น__attribute__ ((cold))
บนmy::assertion_failed
หรือ__builtin_expect(…, false)
ใน!(CONDITION)
การลดค่าใช้จ่ายของการยืนยันผ่าน ในรุ่นที่วางจำหน่ายคุณอาจพิจารณาแทนที่การเรียกใช้ฟังก์ชันเป็นmy::assertion_failed
ด้วยสิ่งที่ต้องการ__builtin_trap
ลดการพิมพ์เท้าที่ไม่สะดวกในการสูญเสียข้อความวินิจฉัย
การเพิ่มประสิทธิภาพแบบนี้มีความเกี่ยวข้องเฉพาะในการยืนยันราคาถูกมาก (เช่นการเปรียบเทียบจำนวนเต็มสองจำนวนที่ได้รับเป็นอาร์กิวเมนต์แล้ว) ในฟังก์ชันที่ตัวเองมีขนาดกะทัดรัดมากโดยไม่พิจารณาขนาดเพิ่มเติมของไบนารีที่สะสมโดยการรวมสตริงข้อความทั้งหมด
เปรียบเทียบวิธีการใช้รหัสนี้
int
positive_difference_1st(const int a, const int b) noexcept
{
if (!(a > b))
my::assertion_failed(__FILE__, __LINE__, __FUNCTION__, "!(a > b)");
return a - b;
}
รวบรวมในการชุมนุมต่อไปนี้
_ZN4test23positive_difference_1stEii:
.LFB0:
.cfi_startproc
cmpl %esi, %edi
jle .L5
movl %edi, %eax
subl %esi, %eax
ret
.L5:
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $.LC0, %ecx
movl $_ZZN4test23positive_difference_1stEiiE12__FUNCTION__, %edx
movl $50, %esi
movl $.LC1, %edi
call _ZN2my16assertion_failedEPKciS1_S1_
.cfi_endproc
.LFE0:
ในขณะที่รหัสต่อไปนี้
int
positive_difference_2nd(const int a, const int b) noexcept
{
if (__builtin_expect(!(a > b), false))
__builtin_trap();
return a - b;
}
ให้แอสเซมบลีนี้
_ZN4test23positive_difference_2ndEii:
.LFB1:
.cfi_startproc
cmpl %esi, %edi
jle .L8
movl %edi, %eax
subl %esi, %eax
ret
.p2align 4,,7
.p2align 3
.L8:
ud2
.cfi_endproc
.LFE1:
ซึ่งฉันรู้สึกสะดวกสบายมากขึ้นด้วย (ตัวอย่างถูกทดสอบกับ GCC 5.3.0 โดยใช้-std=c++14
, -O3
และตั้ง-march=native
ค่าสถานะบน 4.3.3-2-ARCH x86_64 GNU / Linux. ไม่แสดงในตัวอย่างข้างต้นเป็นการประกาศtest::positive_difference_1st
และtest::positive_difference_2nd
ที่ฉันเพิ่มลง__attribute__ ((hot))
ไปmy::assertion_failed
ถูกประกาศด้วย__attribute__ ((cold))
.)
ยืนยันเงื่อนไขเบื้องต้นในฟังก์ชั่นที่ขึ้นอยู่กับพวกเขา
สมมติว่าคุณมีฟังก์ชันต่อไปนี้พร้อมสัญญาที่ระบุ
/**
* @brief
* Counts the frequency of a letter in a string.
*
* The frequency count is case-insensitive.
*
* If `text` does not point to a NUL terminated character array or `letter`
* is not in the character range `[A-Za-z]`, the behavior is undefined.
*
* @param text
* text to count the letters in
*
* @param letter
* letter to count
*
* @returns
* occurences of `letter` in `text`
*
*/
std::size_t
count_letters(const char * text, int letter) noexcept;
แทนที่จะเขียน
assert(text != nullptr);
assert((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z'));
const auto frequency = count_letters(text, letter);
ที่แต่ละไซต์การโทรให้วางตรรกะนั้นลงในคำจำกัดความของ count_letters
std::size_t
count_letters(const char *const text, const int letter) noexcept
{
assert(text != nullptr);
assert((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z'));
auto frequency = std::size_t {};
// TODO: Figure this out...
return frequency;
}
และเรียกมันโดยไม่ต้องกังวลใจต่อ
const auto frequency = count_letters(text, letter);
ซึ่งมีข้อดีดังต่อไปนี้
- คุณต้องเขียนรหัสยืนยันเพียงครั้งเดียว เนื่องจากวัตถุประสงค์ของฟังก์ชั่นนั้นเรียกได้ว่าบ่อยครั้งมากกว่าหนึ่งครั้งสิ่งนี้ควรลดจำนวน
assert
คำสั่งโดยรวมในโค้ดของคุณ
- มันเก็บตรรกะที่ตรวจสอบปัจจัยพื้นฐานใกล้กับตรรกะที่ขึ้นอยู่กับพวกเขา ฉันคิดว่านี่เป็นสิ่งสำคัญที่สุด หากลูกค้าของคุณใช้อินเทอร์เฟซของคุณในทางที่ผิดพวกเขาไม่สามารถสันนิษฐานได้ว่าจะใช้การยืนยันที่ถูกต้องอย่างใดอย่างหนึ่งดังนั้นจึงเป็นการดีกว่าที่ฟังก์ชั่นจะบอกพวกเขา
ข้อเสียที่ชัดเจนคือคุณจะไม่ได้รับตำแหน่งแหล่งที่มาของไซต์การโทรในข้อความการวินิจฉัย ฉันเชื่อว่านี่เป็นปัญหาเล็กน้อย ผู้ดีบั๊กควรจะสามารถให้คุณย้อนกลับไปที่ต้นกำเนิดของการละเมิดสัญญาได้อย่างสะดวก
ความคิดเดียวกันนี้ใช้กับฟังก์ชั่น“ พิเศษ” เช่นตัวดำเนินการที่ทำงานหนักเกินไป เมื่อฉันเขียนตัววนซ้ำฉันมักจะ - ถ้าธรรมชาติของตัววนซ้ำอนุญาต - ให้ฟังก์ชันสมาชิก
bool
good() const noexcept;
ที่อนุญาตให้ถามว่าปลอดภัยหรือไม่ที่จะตรวจสอบตัววนซ้ำ (แน่นอนในทางปฏิบัติมันเกือบจะเป็นไปได้เสมอเท่านั้นที่จะรับประกันได้ว่ามันจะไม่ปลอดภัยที่จะตรวจสอบซ้ำตัววนซ้ำ แต่ฉันเชื่อว่าคุณยังคงสามารถจับข้อบกพร่องจำนวนมากด้วยฟังก์ชั่นดังกล่าว) แทนที่จะทิ้งรหัสทั้งหมดของฉัน ที่ใช้ตัววนซ้ำกับassert(iter.good())
คำสั่งฉันอยากจะใส่assert(this->good())
บรรทัดเดียวเป็นบรรทัดแรกoperator*
ในการทำให้ตัววนซ้ำ
หากคุณใช้ไลบรารีมาตรฐานแทนที่จะทำการตรวจสอบด้วยตนเองตามเงื่อนไขในซอร์สโค้ดของคุณให้เปิดการตรวจสอบในการสร้างการดีบัก พวกเขาสามารถทำการตรวจสอบที่ซับซ้อนยิ่งขึ้นเช่นการทดสอบว่าคอนเทนเนอร์ตัววนซ้ำอ้างอิงถึงยังคงมีอยู่หรือไม่ (ดูเอกสารประกอบสำหรับlibstdc ++และlibc ++ (อยู่ระหว่างดำเนินการ) สำหรับข้อมูลเพิ่มเติม)
ปัจจัยเงื่อนไขทั่วไปออกมา
สมมติว่าคุณกำลังเขียนแพ็คเกจพีชคณิตเชิงเส้น ฟังก์ชั่นหลายอย่างจะมีเงื่อนไขที่ซับซ้อนและการละเมิดมักจะทำให้เกิดผลลัพธ์ที่ไม่ถูกต้องซึ่งไม่สามารถจดจำได้ทันที มันจะดีมากถ้าฟังก์ชั่นเหล่านี้ยืนยันเงื่อนไขเบื้องต้นของพวกเขา หากคุณกำหนดเพรดิเคตที่บอกคุณสมบัติบางอย่างเกี่ยวกับโครงสร้างการยืนยันเหล่านั้นจะอ่านได้ง่ายขึ้น
template <typename MatrixT>
auto
cholesky_decompose(MatrixT&& m)
{
assert(is_square(m) && is_symmetric(m));
// TODO: Somehow decompose that thing...
}
มันจะให้ข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์มากกว่า
cholesky.hxx:357: cholesky_decompose: assertion failed: is_symmetric(m)
ช่วยได้มากกว่าพูด
detail/basic_ops.hxx:1289: fast_compare: assertion failed: m(i, j) == m(j, i)
ก่อนอื่นคุณต้องไปดูซอร์สโค้ดในบริบทเพื่อหาว่าอะไรที่ถูกทดสอบจริง
หากคุณมีclass
ค่าคงที่ที่ไม่น่ารำคาญคุณควรยืนยันกับพวกเขาเป็นครั้งคราวเมื่อคุณยุ่งกับสถานะภายในและต้องการให้แน่ใจว่าคุณออกจากวัตถุในสถานะที่ถูกต้องเมื่อกลับมา
เพื่อจุดประสงค์นี้ผมพบว่ามันมีประโยชน์ในการกำหนดฟังก์ชันสมาชิกที่ฉันอัตภาพเรียกprivate
class_invaraiants_hold_
สมมติว่าคุณกำลังนำไปใช้อีกครั้งstd::vector
(เพราะเราทุกคนรู้ว่ามันไม่ดีพอ) มันอาจมีฟังก์ชั่นเช่นนี้
template <typename T>
bool
vector<T>::class_invariants_hold_() const noexcept
{
if (this->size_ > this->capacity_)
return false;
if ((this->size_ > 0) && (this->data_ == nullptr))
return false;
if ((this->capacity_ == 0) != (this->data_ == nullptr))
return false;
return true;
}
สังเกตเห็นบางสิ่งเกี่ยวกับเรื่องนี้
- ฟังก์ชั่นตัวเองเป็นคำกริยา
const
และnoexcept
สอดคล้องกับแนวทางที่ยืนยันจะไม่มีผลข้างเคียง ถ้ามันสมเหตุสมผลก็จงประกาศconstexpr
เช่นกัน
- ภาคแสดงไม่ได้ยืนยันอะไรเลย มันมีความหมายที่จะเรียกว่าภายใน
assert(this->class_invariants_hold_())
ยืนยันเช่น ด้วยวิธีนี้ถ้าการรวบรวมคำยืนยันนั้นเรามั่นใจได้ว่าจะไม่มีค่าใช้จ่ายในการดำเนินการเกิดขึ้น
- การควบคุมการไหลภายในฟังก์ชั่นนั้นแยกออกเป็นหลาย
if
ประโยคด้วยต้นreturn
s แทนที่จะเป็นนิพจน์ขนาดใหญ่ สิ่งนี้ทำให้ง่ายต่อการก้าวผ่านฟังก์ชั่นในตัวดีบั๊กเกอร์และค้นหาว่าส่วนใดของค่าคงที่ที่ถูกทำลายหากการยืนยันเกิดเพลิงไหม้
อย่าอ้างสิทธิ์ในสิ่งที่โง่
บางสิ่งก็ไม่สมเหตุสมผลที่จะยืนยัน
auto numbers = std::vector<int> {};
numbers.push_back(14);
numbers.push_back(92);
assert(numbers.size() == 2); // silly
assert(!numbers.empty()); // silly and redundant
การยืนยันเหล่านี้ไม่ได้ทำให้รหัสแม้แต่น้อยอ่านง่ายขึ้นหรือง่ายขึ้นที่จะให้เหตุผลเกี่ยวกับ โปรแกรมเมอร์ C ++ ทุกคนควรมีความมั่นใจมากพอstd::vector
ที่จะแน่ใจได้อย่างไรว่าโค้ดด้านบนนั้นถูกต้องเพียงแค่ดูที่มัน ฉันไม่ได้บอกว่าคุณไม่ควรยืนยันขนาดของภาชนะ หากคุณเพิ่มหรือลบองค์ประกอบที่ใช้โฟลว์การควบคุมที่ไม่น่าสนใจการยืนยันดังกล่าวอาจมีประโยชน์ แต่ถ้าเพียงแค่ทำซ้ำสิ่งที่เขียนในรหัสที่ไม่ใช่การยืนยันข้างต้นจะไม่มีค่าที่ได้รับ
ยังไม่ยืนยันว่าฟังก์ชั่นห้องสมุดทำงานอย่างถูกต้อง
auto w = widget {};
w.enable_quantum_mode();
assert(w.quantum_mode_enabled()); // probably silly
หากคุณเชื่อถือห้องสมุดเล็ก ๆ น้อย ๆ ให้พิจารณาใช้ห้องสมุดอื่นแทน
ในทางตรงกันข้ามหากเอกสารของห้องสมุดไม่ชัดเจน 100% และคุณมั่นใจในสัญญาของตนโดยการอ่านซอร์สโค้ดมันสมเหตุสมผลมากที่จะยืนยันว่า“ สัญญาที่อนุมาน” หากห้องสมุดแตกเวอร์ชันอนาคตคุณจะสังเกตเห็นได้อย่างรวดเร็ว
auto w = widget {};
// After reading the source code, I have concluded that quantum mode is
// always off by default but this isn't documented anywhere.
assert(!w.quantum_mode_enabled());
ดีกว่าโซลูชันต่อไปนี้ซึ่งจะไม่บอกคุณว่าสมมติฐานของคุณถูกต้องหรือไม่
auto w = widget {};
if (w.quantum_mode_enabled())
{
// I don't think that quantum mode is ever enabled by default but
// I'm not sure.
w.disable_quantum_mode();
}
อย่าละเมิดคำยืนยันในการใช้ตรรกะของโปรแกรม
ควรใช้การยืนยันเพื่อเปิดเผยข้อบกพร่องที่ควรค่าแก่การฆ่าแอปพลิเคชันของคุณในทันที พวกเขาไม่ควรใช้เพื่อตรวจสอบเงื่อนไขอื่น ๆ แม้ว่าปฏิกิริยาที่เหมาะสมกับสภาพนั้นก็จะถูกยกเลิกทันที
ดังนั้นเขียนสิ่งนี้ ...
if (!server_reachable())
{
log_message("server not reachable");
shutdown();
}
…แทนที่จะเป็นเช่นนั้น
assert(server_reachable());
นอกจากนี้ยังไม่เคยใช้ยืนยันในการตรวจสอบการป้อนข้อมูลที่ไม่น่าเชื่อถือหรือตรวจสอบว่าstd::malloc
ไม่ได้คุณreturn
nullptr
แม้ว่าคุณจะรู้ว่าคุณจะไม่ปิดการยืนยันแม้ในรุ่นที่วางจำหน่ายการยืนยันจะสื่อสารกับผู้อ่านว่ามันตรวจสอบบางสิ่งที่เป็นจริงเสมอเนื่องจากโปรแกรมไม่มีข้อบกพร่องและไม่มีผลข้างเคียงที่มองเห็นได้ หากนี่ไม่ใช่ข้อความที่คุณต้องการสื่อสารให้ใช้กลไกการจัดการข้อผิดพลาดทางเลือกเช่นthrow
การยกเว้น หากคุณพบว่าสะดวกที่จะมีตัวห่อหุ้มแมโครสำหรับการตรวจสอบการไม่ยืนยันของคุณ อย่าเรียกว่า "ยืนยัน", "สมมติ", "ต้องการ", "มั่นใจ" หรืออะไรทำนองนั้น ตรรกะภายในของมันอาจเหมือนกันassert
ยกเว้นว่ามันจะไม่ถูกรวบรวมไว้แน่นอน
ข้อมูลมากกว่านี้
ผมพบว่าจอห์น Lakos' พูดคุยโปรแกรมป้องกัน Done Rightรับที่ CppCon'14 ( 1 เซนต์ส่วน , 2 ครั้งส่วนหนึ่ง ) enlightening มาก เขาใช้ความคิดในการปรับแต่งสิ่งที่ยืนยันถูกเปิดใช้งานและวิธีการตอบสนองต่อข้อยกเว้นที่ล้มเหลวยิ่งกว่าที่ฉันทำในคำตอบนี้