c++ - एक गैर-टेम्प्लेटेड फ़ंक्शन पर एक बाधा अभिव्यक्ति का क्या मतलब है?




language-lawyer c++20 (2)

[temp.constr.decl] का कहना है कि हम एक टेम्पलेट या एक फ़ंक्शन को बाधा अभिव्यक्ति के साथ बाधित कर सकते हैं।

घोषणाकर्ता [dcl.decl] हमें बताता है कि, फ़ंक्शंस के लिए, हम एक वैकल्पिक ट्रेलिंग जोड़ सकते हैं, इसके लिए क्लॉज़ को बाध्य करने की आवश्यकता होती है, और मानक ड्राफ्ट n4820 यहां तक ​​कि इन (प्रतीत होता है कि व्यर्थ) उदाहरण देता है:

void f1(int a) requires true;
auto f2(int a) -> bool requires true;

मैं समझता हूं कि किसी टेम्प्लेट या अवधारणा को समझना उपयोगी है, लेकिन मैं यह देखने में विफल रहता हूं कि ये अवरोध गैर-अस्थायी कार्यों के लिए कैसे उपयोगी हैं। एक गैर-अस्थायी कार्य में बाधा डालने की बात क्या है?


गैर-टेम्पलेट फ़ंक्शंस को बाध्य करने के मुख्य बिंदुओं में से एक टेम्प्लेट कक्षाओं के गैर-टेम्प्लेट सदस्यों के लिए बाधाओं को लिखने में सक्षम होना है। उदाहरण के लिए, आपके पास कुछ इस प्रकार हो सकता है:

template<typename T>
class value
{
public:
  value(const T& t);
  value(T&& t);

private:
  T t_;
};

अब, आप चाहते हैं कि value T से कॉपी योग्य / जंगम हो। लेकिन वास्तव में, आप चाहते हैं कि यह T से कॉपी योग्य / जंगम हो, जहां तक ​​कि T खुद ही कॉपी करने योग्य / जंगम हो। तो आप इसे कैसे करते हैं?

पूर्व-बाधाओं, आपको मेटा-प्रोग्रामिंग हैकरी का एक गुच्छा लिखना होगा। हो सकता है कि आप इन कंस्ट्रक्टर्स को बनाते हैं, जिसके लिए यह आवश्यक है कि कॉपी / मूव की आवश्यकता के अलावा, दिए गए प्रकार U भी T जैसा ही हो। या आपको एक बेस क्लास लिखना होगा जो आपको विरासत में मिला है, जिसमें T की कॉपी / चालकता के आधार पर अलग-अलग विशेषज्ञता है।

अड़चनें हैं, आप ऐसा करते हैं:

template<typename T>
class value
{
public:
  value(const T& t) requires is_copy_constructible_v<T> : t_(t) {}
  value(T&& t) requires is_move_constructible_v<T> : t_(std::move(t)) {}

private:
  T t_;
};

कोई हैकरी नहीं। उन कार्यों के लिए कोई टेम्प्लेट लागू नहीं करना चाहिए जिन्हें टेम्प्लेट होने की आवश्यकता नहीं है। यह सिर्फ काम करता है, और उपयोगकर्ता के लिए यह समझना आसान है कि क्या हो रहा है।

यह उन कार्यों के लिए विशेष रूप से महत्वपूर्ण है जो टेम्पलेट नहीं हो सकते । एक कंस्ट्रक्टर को कॉपी या मूव कंस्ट्रक्टर मानने के लिए, यह एक टेम्प्लेट नहीं हो सकता है। वही कॉपी / मूव असाइनमेंट ऑपरेटर्स के लिए जाता है। लेकिन ऐसी चीजों में अड़चनें आ सकती हैं।


जिस प्रकार एक अवधारणा निम्नलिखित उदाहरण पर विचार करती है

#include <iostream>

void f( long x ) requires ( sizeof( long ) == sizeof( int ) )
{
    std::cout << "Bye " << x << '\n';
}

void f( long long x ) requires ( sizeof( long ) == sizeof( long long ) )
{
    std::cout << "Hello " << x << '\n';
}

int main() 
{
    f( 0l );
}

अगर sizeof( long ) == sizeof( long long ) तो प्रोग्राम आउटपुट होगा

Hello 0

अन्यथा

Bye 0

उदाहरण के लिए, आप एक फ़ंक्शन में ऐसे दृष्टिकोण का उपयोग कर सकते हैं जो लूप पुनरावृत्तियों की संख्या को प्रतिबंधित करने या अपवाद को फेंकने के लिए भाज्य की गणना करता है।

यहाँ एक प्रदर्शन कार्यक्रम है।

#include <iostream>
#include <stdexcept>

unsigned long factorial( unsigned long n ) noexcept( false ) 
    requires ( sizeof( unsigned long ) == sizeof( unsigned int ) )
{
    const unsigned long MAX_STEPS = 12;

    if ( MAX_STEPS < n ) throw std::out_of_range( "Too big value." );

    unsigned long f = 1;

    for ( unsigned long i = 1; i < n; i++ ) f *= ( i + 1 );

    return f;
}

unsigned long long factorial( unsigned long long n ) noexcept( false ) 
    requires ( sizeof( unsigned long ) == sizeof( unsigned long long ) )
{
    const unsigned long long MAX_STEPS = 20;

    if ( MAX_STEPS < n ) throw std::out_of_range( "Too big value." );

    unsigned long f = 1;

    for ( unsigned long long i = 1; i < n; i++ ) f *= ( i + 1 );

    return f;
}

int main() 
{
    unsigned long n = 20;

    try
    {
        std::cout << factorial( n ) << '\n';
    }
    catch ( const std::out_of_range &ex )
    {
        std::cout << ex.what() << '\n';
    }
}

इसका आउटपुट या तो हो सकता है

2432902008176640000

या

Too big value.




c++20