Static_assert क्या करता है, और आप इसके लिए क्या उपयोग करेंगे?


117

क्या आप एक उदाहरण दे सकते हैं जहां static_assert(...)('C ++ 11') हाथ में समस्या को हल कर सकेगा?

मैं रन-टाइम से परिचित हूं assert(...)। मुझे static_assert(...)नियमित रूप से कब पसंद करना चाहिए assert(...)?

इसके अलावा, boostवहाँ कुछ कहा जाता है BOOST_STATIC_ASSERT, यह उसी के रूप में है static_assert(...)?


BOOST_MPL_ASSERT, BOOST_MPL_ASSERT_NOT, BOOST_MPL_ASSERT_MSG, BOOST_MPL_ASSERT_RELATION [यह भी देखें: boost.org/doc/libs/1_40_0/libs/mpl/doc/refmanual/asserts.html] अधिक विकल्पों के लिए। _MSG विशेष रूप से अच्छा है जब आप इसका उपयोग करने का पता लगाते हैं।
KitsuneYMG

जवाबों:


82

मेरे सर के ऊपर से चला गया...

#include "SomeLibrary.h"

static_assert(SomeLibrary::Version > 2, 
         "Old versions of SomeLibrary are missing the foo functionality.  Cannot proceed!");

class UsingSomeLibrary {
   // ...
};

यह मानते हुए कि डी SomeLibrary::Versionहोने के बजाय एक स्थैतिक कांस्टेबल के रूप में घोषित किया गया है #define(जैसा कि एक C ++ लाइब्रेरी में होगा)।

वास्तव में संकलित करने के लिए होने के साथ कंट्रास्ट SomeLibraryऔर अपने कोड, लिंक सब कुछ है, और चलाने के लिए निष्पादन योग्य केवल तब पता लगाने के लिए कि आप 30 मिनट खर्च का कोई असंगत संस्करण संकलन SomeLibrary

@ अराक, आपकी टिप्पणी के जवाब में: हां, आप static_assertइसे देखने के स्थान से कहीं भी बैठ सकते हैं :

class Foo
{
    public: 
        static const int bar = 3;
};

static_assert(Foo::bar > 4, "Foo::bar is too small :(");

int main()
{ 
    return Foo::bar;
}
$ g ++ --std = c ++ 0x a.cpp
a.cpp: 7: error: स्टेटिक अस्सिटेंस विफल: "फू :: बार बहुत छोटा है :("

1
मैं थोड़ा भ्रमित हूं, क्या आप static_assertगैर-निष्पादन के संदर्भ में रख सकते हैं ? यह एक बहुत अच्छा उदाहरण लगता है :)
अर्क

3
हाँ, स्थैतिक मुखर के रूप में वे आम तौर पर लागू किया जाता है एक वस्तु बनाने के रूप में कि केवल परिभाषित किया गया है यदि विधेय सत्य है। यह सिर्फ एक वैश्विक बना देगा।
GMANNICKG

मुझे यकीन नहीं है कि यह पूरी तरह से मूल प्रश्न का उत्तर देने के योग्य है, लेकिन अच्छा प्रदर्शन
मैट जॉइनर

2
क्या बीच का अंतर है इस जवाब पर कोई विवरण प्रदान नहीं करता है ज़ोर से <cassert> और static_assert
bitek

11
@monocoder: "कंट्रास्ट विद ..." से शुरू होने वाले पैराग्राफ को देखें। संक्षेप में: ज़ोर चेकों कार्यावधि में इसकी हालत, और static_assert चेकों संकलन में इसकी हालत। इसलिए यदि आप जो शर्त लगा रहे हैं वह संकलन समय पर जानी जाती है, तो उपयोग करें static_assert। यदि प्रोग्राम चलने तक स्थिति ज्ञात नहीं होगी, तो उपयोग करें assert
माइक डीमोन

131

संकलन समय पर जोर देने के लिए स्टैटिक एस्टर का उपयोग किया जाता है। जब स्थिर अभिकथन विफल हो जाता है, तो प्रोग्राम केवल संकलन नहीं करता है। , जैसे उदाहरण के लिए, यदि आप कुछ कार्यक्षमता कोड है कि गंभीर रूप से पर निर्भर करता है के द्वारा लागू इस, विभिन्न स्थितियों में उपयोगी है unsigned intवास्तव में 32 बिट होने वस्तु। आप इस तरह से एक स्थिर मुखर डाल सकते हैं

static_assert(sizeof(unsigned int) * CHAR_BIT == 32);

आपके कोड में एक अन्य मंच पर, अलग-अलग आकार के unsigned intप्रकार के साथ संकलन विफल हो जाएगा, इस प्रकार डेवलपर को कोड के समस्याग्रस्त हिस्से पर ध्यान आकर्षित करना और इसे फिर से लागू करने या फिर से निरीक्षण करने की सलाह देना।

एक और उदाहरण के लिए, आप void *एक फ़ंक्शन (एक हैक, लेकिन कई बार उपयोगी) के लिए एक सूचक के रूप में कुछ अभिन्न मूल्य को पारित करना चाह सकते हैं और आप यह सुनिश्चित करना चाहते हैं कि अभिन्न मूल्य सूचक में फिट होगा

int i;

static_assert(sizeof(void *) >= sizeof i);
foo((void *) i);

आप चाहते हैं कि उस charप्रकार की संपत्ति पर हस्ताक्षर किए जाएं

static_assert(CHAR_MIN < 0);

या कि नकारात्मक मूल्यों के साथ अभिन्न विभाजन शून्य की ओर गोल है

static_assert(-5 / 2 == -2);

और इसी तरह।

कई मामलों में रन-टाइम अभिक्रियाओं का उपयोग स्थैतिक अभिकथनों के बजाय किया जा सकता है, लेकिन रन-टाइम दावे केवल रन-टाइम पर काम करते हैं और केवल तभी जब नियंत्रण अभिकथन पर गुजरता है। इस कारण से एक असफल रन-टाइम अभिकर्मक निष्क्रिय हो सकता है, विस्तारित अवधि के लिए अनिर्धारित।

बेशक, स्थैतिक अभिकथन में अभिव्यक्ति एक संकलन-समय स्थिर होना है। यह रन-टाइम मान नहीं हो सकता। रन-टाइम मान के लिए आपके पास कोई अन्य विकल्प नहीं है, लेकिन साधारण का उपयोग करें assert


3
क्या एक दूसरे पैरामीटर के रूप में एक स्ट्रिंग शाब्दिक होने के लिए static_assert की आवश्यकता नहीं है?
ट्रेवर हिक्की

3
@ ट्रेवर हिक्की: हाँ, यह है। लेकिन मैं static_assertC ++ 11 से विशेष रूप से संदर्भित करने का प्रयास नहीं कर रहा था । मेरे static_assertऊपर स्थैतिक अभिकथन का केवल कुछ अमूर्त कार्यान्वयन है। (मैं व्यक्तिगत रूप से सी कोड में ऐसा कुछ उपयोग करता हूं)। मेरा उत्तर स्थैतिक अभिकथन के सामान्य उद्देश्य और रन-टाइम अभिकथन से उनके अंतर के बारे में है।
चींटी

पहले उदाहरण में, आप मान रहे हैं कि प्रकार के चर में कोई पैडिंग बिट्स नहीं है unsigned int। यह मानक द्वारा गारंटी नहीं है। प्रकार का एक चर unsigned int32 प्रकार की स्मृति पर कब्जा कर सकता है, उनमें से 16 को अप्रयुक्त कर सकता है (और इस तरह मैक्रो UINT_MAXबराबर होगा 65535)। जिस तरह से तो आप पहली बार स्थिर दावा ( "का वर्णन unsigned intवस्तु होने वास्तव में 32 बिट") भ्रामक है। अपने विवरण से मेल करने के लिए, इस दावे के साथ-साथ शामिल किया जाना चाहिए: static_assert(UINT_MAX >= 0xFFFFFFFFu)
राल्फ्स

@TrevorHickey अब और नहीं (C ++ 17)
luizfls

13

मैं इसका उपयोग संकलक व्यवहार, हेडर, लिबास और यहां तक ​​कि अपने स्वयं के कोड के बारे में मेरी मान्यताओं को सुनिश्चित करने के लिए करता हूं। उदाहरण के लिए यहां मैं यह सत्यापित करता हूं कि संरचना को अपेक्षित आकार में सही ढंग से पैक किया गया है।

struct LogicalBlockAddress
{
#pragma pack(push, 1)
    Uint32 logicalBlockNumber;
    Uint16 partitionReferenceNumber;
#pragma pack(pop)
};
BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6);

एक वर्ग रैपिंग stdio.hके साथ fseek(), मैंने कुछ शॉर्टकट्स लिए हैं enum Originऔर यह जांचा है कि उन शॉर्टकट्स को परिभाषित स्थिरांक के साथ संरेखित किया गया हैstdio.h

uint64_t BasicFile::seek(int64_t offset, enum Origin origin)
{
    BOOST_STATIC_ASSERT(SEEK_SET == Origin::SET);

आप को प्राथमिकता देनी चाहिए static_assertसे अधिक assertजब व्यवहार में इस तरह के उदाहरण मैं ऊपर दिया है के रूप में, संकलन समय पर परिभाषित किया गया है, और रनटाइम पर नहीं। एक उदाहरण जहां यह मामला नहीं है , जिसमें पैरामीटर और रिटर्न कोड की जांच शामिल होगी।

BOOST_STATIC_ASSERTएक पूर्व-सी ++ 0x मैक्रो है जो अगर अवैध कोड को उत्पन्न करता है, तो शर्त संतुष्ट नहीं होती है। इरादे समान हैं, यद्यपि static_assertमानकीकृत है और बेहतर संकलक निदान प्रदान कर सकता है।


9

BOOST_STATIC_ASSERTstatic_assertकार्यक्षमता के लिए एक क्रॉस प्लेटफ़ॉर्म रैपर है।

वर्तमान में मैं एक वर्ग पर "अवधारणाओं" को लागू करने के लिए static_assert का उपयोग कर रहा हूं।

उदाहरण:

template <typename T, typename U>
struct Type
{
  BOOST_STATIC_ASSERT(boost::is_base_of<T, Interface>::value);
  BOOST_STATIC_ASSERT(std::numeric_limits<U>::is_integer);
  /* ... more code ... */
};

यह एक संकलित समय त्रुटि का कारण होगा यदि उपरोक्त शर्तों में से कोई भी पूरा नहीं हुआ है।


3
अब जब C ++ 11 आउट हो गया है (और थोड़ी देर के लिए बाहर हो गया है), static_assert को सभी प्रमुख कंपाइलरों के अधिक हाल के संस्करणों द्वारा समर्थित किया जाना चाहिए। हममें से जो C ++ 14 का इंतजार नहीं कर सकते हैं (जिनमें उम्मीद है कि खाका बाधाएं होंगी), यह static_assert का एक बहुत ही उपयोगी अनुप्रयोग है।
कॉलिन

7

एक उपयोग static_assertयह सुनिश्चित करने के लिए हो सकता है कि एक संरचना (जो बाहरी दुनिया के साथ एक इंटरफ़ेस है, जैसे कि एक नेटवर्क या फ़ाइल) बिल्कुल वही आकार है जो आप अपेक्षा करते हैं। यह उन मामलों को पकड़ता है जहां कोई व्यक्ति परिणामों को साकार किए बिना संरचना से किसी सदस्य को जोड़ता या संशोधित करता है। static_assertयह ले सकते हैं और उपयोगकर्ता को सचेत करेंगे।


3

अवधारणाओं के अभाव में कोई भी static_assertसरल और पठनीय संकलन-समय प्रकार की जाँच कर सकता है, उदाहरण के लिए, टेम्प्लेट में:

template <class T>
void MyFunc(T value)
{
static_assert(std::is_base_of<MyBase, T>::value, 
              "T must be derived from MyBase");

// ...
}

2

यह सीधे मूल प्रश्न का उत्तर नहीं देता है, लेकिन C ++ 11 से पहले इन संकलित समय जांचों को लागू करने के तरीके में एक दिलचस्प अध्ययन करता है।

आंद्रेई अलेक्जेंडर्सकु द्वारा मॉडर्न C ++ डिज़ाइन का अध्याय 2 (धारा 2.1) इस तरह के संकलन-समय के सिद्धांत को लागू करता है

template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};

#define STATIC_CHECK(expr, msg) \
{ CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

मैक्रो STATIC_CHECK () और static_assert () की तुलना करें

STATIC_CHECK(0, COMPILATION_FAILED);
static_assert(0, "compilation failed");

-2

static_assertके प्रयोग की मनाही करने के लिए इस्तेमाल किया जा सकता deleteकीवर्ड इस तरह:

#define delete static_assert(0, "The keyword \"delete\" is forbidden.");

प्रत्येक आधुनिक C ++ डेवलपर यह करना चाहता है कि यदि वह केवल क्लास एस्क और स्ट्रक्चर एस का उपयोग करके रूढ़िवादी कचरा कलेक्टर का उपयोग करना चाहता है, जो ऑपरेटर को ओवरलोड करने के लिए एक फ़ंक्शन को आमंत्रित करने के लिए नया लोड करता है जो रूढ़िवादी कचरा कलेक्टर के रूढ़िवादी ढेर पर स्मृति आवंटित करता है। फ़ंक्शन के प्रारंभ में ऐसा करने वाले कुछ फ़ंक्शन को प्रारंभ करके प्रारंभ और त्वरित किया जा सकता है main

उदाहरण के लिए हर आधुनिक C ++ डेवलपर जो बोहम-डेमर्स-वेसर रूढ़िवादी कचरा कलेक्टर का उपयोग करना चाहता है, वह mainफ़ंक्शन लेखन की शुरुआत में करेगा :

GC_init();

और हर तरह से classऔर इस तरह से structओवरलोड करें operator new:

void* operator new(size_t size)
{
     return GC_malloc(size);
}

और अब operator deleteइसकी आवश्यकता नहीं है, क्योंकि बोहेम-डेमर्स-वेसर रूढ़िवादी कचरा कलेक्टर दोनों के लिए स्वतंत्र है और मेमोरी के हर ब्लॉक को डील करता है जब इसकी आवश्यकता नहीं होती है, तो डेवलपर deleteकीवर्ड को रोकना चाहता है ।

एक तरीका delete operatorइस तरह से ओवरलोडिंग है :

void operator delete(void* ptr)
{
    assert(0);
}

लेकिन यह अनुशंसित नहीं है, क्योंकि आधुनिक सी ++ डेवलपर को पता चल जाएगा कि उसने गलती से delete operatorरन टाइम पर आह्वान किया है , लेकिन संकलन समय पर जल्द ही यह जानना बेहतर है।

तो मेरी राय में इस परिदृश्य का सबसे अच्छा समाधान static_assertइस उत्तर की शुरुआत में दिखाए गए अनुसार उपयोग करना है ।

बेशक, यह भी किया जा सकता है BOOST_STATIC_ASSERT, लेकिन मुझे लगता है कि static_assertयह बेहतर है और हमेशा अधिक पसंद किया जाना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.