std :: सक्षम_if শর্তাধীন সদস্য ফাংশন সংকলন করতে


156

আমি বুঝতে কিভাবে ব্যবহার করতে কাজ করার জন্য একটি সহজ উদাহরণ পেতে চেষ্টা করছি std::enable_if। আমি এই উত্তরটি পড়ার পরে , আমি ভেবেছিলাম একটি সহজ উদাহরণ নিয়ে আসা খুব কঠিন হওয়া উচিত নয়। আমি std::enable_ifদুটি সদস্য-ফাংশনের মধ্যে বেছে নিতে ব্যবহার করতে চাই এবং তাদের মধ্যে কেবল একটিরই ব্যবহার করার অনুমতি দেব।

দুর্ভাগ্যক্রমে, নিম্নলিখিতটি জিসিসি 4.7 দিয়ে সংকলন করে না এবং ঘন্টার পর ঘন্টা চেষ্টা করার পরে আমি আপনাকে বলছি যে আমার ভুলটি কী।

#include <utility>
#include <iostream>

template< class T >
class Y {

    public:
        template < typename = typename std::enable_if< true >::type >
        T foo() {
            return 10;
        }
        template < typename = typename std::enable_if< false >::type >
        T foo() {
            return 10;
        }

};


int main() {
    Y< double > y;

    std::cout << y.foo() << std::endl;
}

জিসিসি নিম্নলিখিত সমস্যাগুলি প্রতিবেদন করে:

% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x    enable_if.cpp   -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'

জি ++ দ্বিতীয় সদস্য ফাংশনের জন্য ভুল ইনস্ট্যান্টেশন কেন মুছে দেয় না? মান অনুসারে, std::enable_if< bool, T = void >::typeবুলিয়ান টেম্পলেট প্যারামিটারটি সত্য হলেই বিদ্যমান। তবে জি ++ এটিকে SFINAE হিসাবে বিবেচনা করবেন না কেন? আমি মনে করি যে ওভারলোডিং ত্রুটি বার্তাটি এমন সমস্যা থেকে আসে যে g ++ দ্বিতীয় সদস্য ফাংশনটি মুছে না এবং বিশ্বাস করে যে এটি একটি ওভারলোড হওয়া উচিত।


1
আমি নিশ্চিত নই, তবে আমি মনে করি এটি নিম্নলিখিতগুলি: সক্ষম_আইফ SFINAE এর উপর ভিত্তি করে (প্রতিস্থাপনের ব্যর্থতা কোনও ত্রুটি নয়)। তবে, আপনার এখানে কোনও বিকল্প নেই, কারণ কোন ওভারলোড ব্যবহার করবেন তা নির্ধারণের জন্য কোনও পরামিতি ব্যবহার করা যাবে না। আপনার "সত্য" অন "মিথ্যা" টি এর উপর নির্ভরশীল করা উচিত (আমি জানি আপনি এটি সাধারণ উদাহরণে করতে চান নি, তবে এটি এখন খুব সহজ ...)
ফিলিপ

3
আমি সে সম্পর্কেও ভেবেছিলাম এবং ব্যবহার করার চেষ্টা করেছি std::is_same< T, int >::valueএবং ! std::is_same< T, int >::valueযা একই ফলাফল দেয়।
এভানু

উত্তর:


117

SFINAE কেবলমাত্র তখনই কাজ করে যদি কোনও টেম্পলেট যুক্তির আর্গুমেন্ট ছাড়ের ক্ষেত্রে প্রতিস্থাপনটি নির্মাণকে খারাপভাবে গঠন করে। এরকম কোনও বিকল্প নেই।

আমি সে সম্পর্কেও ভেবেছিলাম এবং ব্যবহার করার চেষ্টা করেছি std::is_same< T, int >::valueএবং ! std::is_same< T, int >::valueযা একই ফলাফল দেয়।

এটি কারণ কারণ যখন শ্রেণীর টেম্পলেটটি তাত্ক্ষণিকভাবে চালু হয় (যা আপনি যখন Y<int>অন্যান্য কেসগুলির মধ্যে টাইপের কোনও বস্তু তৈরি করেন তখন ঘটে থাকে ), এটি তার সমস্ত সদস্যের ঘোষণাকে তাত্ক্ষণিক করে তোলে (অগত্যা তাদের সংজ্ঞা / সংস্থা নয়!)। এর মধ্যে এর সদস্য টেম্পলেটগুলিও রয়েছে। নোট যা Tতখন জানা ছিল এবং !std::is_same< T, int >::valueমিথ্যা ফল দেয়। সুতরাং এটি একটি ক্লাস তৈরি করবে Y<int>যা এতে রয়েছে

class Y<int> {
    public:
        /* instantiated from
        template < typename = typename std::enable_if< 
          std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< true >::type >
        int foo();

        /* instantiated from

        template < typename = typename std::enable_if< 
          ! std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< false >::type >
        int foo();
};

std::enable_if<false>::type, একটি অ-বিদ্যমান টাইপ ব্যবহারের যাতে ঘোষণা মন্দ গঠিত হয়। এবং এইভাবে আপনার প্রোগ্রামটি অবৈধ।

আপনাকে সদস্য টেমপ্লেটগুলি সদস্য টেমপ্লেটের enable_ifএকটি প্যারামিটারের উপর নির্ভর করতে হবে। তারপরে ঘোষণাগুলি বৈধ, কারণ পুরো প্রকারটি এখনও নির্ভরশীল। আপনি যখন তাদের কাউকে কল করার চেষ্টা করেন, তখন তাদের টেম্পলেট আর্গুমেন্টগুলির জন্য আর্গুমেন্ট ছাড়ের ঘটনা ঘটে এবং SFINAE প্রত্যাশা অনুযায়ী ঘটে। এই প্রশ্নটি এবং এটি কীভাবে করা যায় তার সম্পর্কিত উত্তর দেখুন ।


14
... কেবল এটি পরিষ্কার করার জন্য, এটি কার্যকর হলে: যখন Yটেম্পলেট শ্রেণীর কোনও উদাহরণ ইনস্ট্যান্ট করা হয়, সংকলকটি আসলে টেমপ্লেটের সদস্য ফাংশনগুলি সংকলন করে না; যাইহোক, Tসংকলকটি সদস্য টেমপ্লেটটি ডিক্লেয়ারেশনগুলির প্রতিস্থাপন সম্পাদন করবে যাতে এই সদস্য টেমপ্লেটগুলি পরবর্তী সময়ে ইনস্ট্যান্ট করা যায়। ব্যর্থতার এই বিন্দুটি SFINAE নয়, কারণ ওভারলোড রেজোলিউশনের জন্য সম্ভাব্য ফাংশনগুলির সেট নির্ধারণ করার সময় কেবলমাত্র SFINAE প্রযোজ্য , এবং কোনও শ্রেণীর তাত্ক্ষণিকতা ওভারলোড রেজোলিউশনের জন্য ফাংশনগুলির একটি সেট নির্ধারণের ক্ষেত্রে নয়। (বা তাই আমার মনে হয়!)
ড্যান নিসেনবাউম

93

আমি এই সংক্ষিপ্ত উদাহরণ তৈরি করেছি যা কাজ করে।

#include <iostream>
#include <type_traits>

class foo;
class bar;

template<class T>
struct is_bar
{
    template<class Q = T>
    typename std::enable_if<std::is_same<Q, bar>::value, bool>::type check()
    {
        return true;
    }

    template<class Q = T>
    typename std::enable_if<!std::is_same<Q, bar>::value, bool>::type check()
    {
        return false;
    }
};

int main()
{
    is_bar<foo> foo_is_bar;
    is_bar<bar> bar_is_bar;
    if (!foo_is_bar.check() && bar_is_bar.check())
        std::cout << "It works!" << std::endl;

    return 0;
}

আপনি আমাকে বিস্তারিত বলতে চাইলে মন্তব্য করুন। আমি মনে করি কোডটি কম বেশি স্ব-বর্ণনামূলক, তবে আবার আমি এটিকে তৈরি করেছিলাম যাতে আমার ভুল হতে পারে :)

আপনি এটি এখানে কর্মে দেখতে পাবেন ।


2
এটি ভিএস ২০১২ তে সংকলন করে না। error C4519: default template arguments are only allowed on a class template
পাইথননট

1
এটা দুর্ভাগ্যজনক. আমি এটি কেবলমাত্র জিসিসি দিয়ে পরীক্ষা করেছি। হয়তো এই সাহায্য করে: stackoverflow.com/a/17543296/660982
jpihl

1
এটি অবশ্যই এখানে সেরা উত্তর এবং ঠিক আমি যা খুঁজছিলাম।
ওয়েপেং এল

3
এর Qসমান হয়েও কেন অন্য একটি টেম্পলেট শ্রেণি তৈরি করার প্রয়োজন আছে T?
ilya1725

1
কারণ আপনাকে testসদস্য ফাংশনটি টেম্পলেট করতে হবে । উভয় একই সময়ে থাকতে পারে না। Qক্লাস টেম্পলেট প্রকারটি কেবল ফরওয়ার্ড করে T। আপনি ক্লাসের টেম্পলেটটি এর Tমতো সরিয়ে ফেলতে পারেন: cpp.sh/4nxw তবে সেই কিন্ডাটি হেরে যায়।
jpihl

13

"সবেমাত্র কাজ করে" এমন সমাধানের সন্ধান করছেন এমন দেরিতে-আগতদের জন্য:

#include <utility>
#include <iostream>

template< typename T >
class Y {

    template< bool cond, typename U >
    using resolvedType  = typename std::enable_if< cond, U >::type; 

    public:
        template< typename U = T > 
        resolvedType< true, U > foo() {
            return 11;
        }
        template< typename U = T >
        resolvedType< false, U > foo() {
            return 12;
        }

};


int main() {
    Y< double > y;

    std::cout << y.foo() << std::endl;
}

এর সাথে সংকলন:

g++ -std=gnu++14 test.cpp 

চলমান দেয়:

./a.out 
11

6
ওম, আপনি কেন নাম পরিবর্তন std::enable_if_tকরবেন resolvedType?
কিওয়ার্টি

1
কারণ সকলেই সি ++ 17 ব্যবহার করতে পারে না কারণগুলি যে ব্যাপকভাবে পরিবর্তিত হতে পারে।
জেমস ইয়াং

9

এই পোস্ট থেকে :

ডিফল্ট টেম্পলেট আর্গুমেন্ট কোনও টেমপ্লেটের স্বাক্ষরের অংশ নয়

তবে কেউ এরকম কিছু করতে পারে:

#include <iostream>

struct Foo {
    template < class T,
               class std::enable_if < !std::is_integral<T>::value, int >::type = 0 >
    void f(const T& value)
    {
        std::cout << "Not int" << std::endl;
    }

    template<class T,
             class std::enable_if<std::is_integral<T>::value, int>::type = 0>
    void f(const T& value)
    {
        std::cout << "Int" << std::endl;
    }
};

int main()
{
    Foo foo;
    foo.f(1);
    foo.f(1.1);

    // Output:
    // Int
    // Not int
}

এটি কাজ করে, তবে এটি মূলত টেম্প্লেটিং ফাংশনগুলি নয়, ক্লাস নিজেই নয় ... এটি দুটি অভিন্ন-প্রোটোটাইপযুক্ত ফাংশনগুলির মধ্যে একটিও ছাড়ার অনুমতি দেয় না (যখন আপনাকে ওভারলোডিং ওভার করার প্রয়োজন হবে)। তবে ধারণাটি দুর্দান্ত is আপনি কি ওপি উদাহরণটি একটি কার্যকরী আকারে আবার লিখতে পারেন, দয়া করে?
ব্যবহারকারী 1284631

5

এই সমস্যাটি সমাধান করার একটি উপায়, সদস্য ফাংশনগুলির বিশেষত্ব হ'ল বিশেষতাকে অন্য ক্লাসে স্থাপন করা, তারপরে সেই শ্রেণি থেকে উত্তরাধিকারী। অন্যান্য অন্তর্নিহিত ডেটার সমস্ত অ্যাক্সেস পেতে আপনাকে উত্তরাধিকারের ক্রম পরিবর্তন করতে হতে পারে তবে এই কৌশলটি কার্যকর হয়।

template< class T, bool condition> struct FooImpl;
template<class T> struct FooImpl<T, true> {
T foo() { return 10; }
};

template<class T> struct FoolImpl<T,false> {
T foo() { return 5; }
};

template< class T >
class Y : public FooImpl<T, boost::is_integer<T> > // whatever your test is goes here.
{
public:
    typedef FooImpl<T, boost::is_integer<T> > inherited;

    // you will need to use "inherited::" if you want to name any of the 
    // members of those inherited classes.
};

এই কৌশলটির অসুবিধাটি হ'ল যদি আপনাকে বিভিন্ন সদস্যের বিভিন্ন কার্যের জন্য বিভিন্ন জিনিস পরীক্ষা করতে হয় তবে আপনাকে প্রত্যেকের জন্য একটি ক্লাস তৈরি করতে হবে এবং উত্তরাধিকারের গাছটিতে এটি শৃঙ্খলাবদ্ধ করতে হবে। এটি সাধারণ ডেটা সদস্যদের অ্যাক্সেসের জন্য সত্য।

উদা:

template<class T, bool condition> class Goo;
// repeat pattern above.

template<class T, bool condition>
class Foo<T, true> : public Goo<T, boost::test<T> > {
public:
    typedef Goo<T, boost::test<T> > inherited:
    // etc. etc.
};

4

বুলিয়ানটি টেম্পলেট প্যারামিটারটি কমানোর উপর নির্ভর করে। তাই ঠিক করার একটি সহজ উপায় হ'ল ডিফল্ট বুলিয়ান প্যারামিটার ব্যবহার করা:

template< class T >
class Y {

    public:
        template < bool EnableBool = true, typename = typename std::enable_if<( std::is_same<T, double>::value && EnableBool )>::type >
        T foo() {
            return 10;
        }

};

তবে আপনি সদস্য ফাংশনটি ওভারলোড করতে চাইলে এটি কাজ করবে না। পরিবর্তে, টিক লাইব্রেরি TICK_MEMBER_REQUIRESথেকে এটি ব্যবহার করা সেরা :

template< class T >
class Y {

    public:
        TICK_MEMBER_REQUIRES(std::is_same<T, double>::value)
        T foo() {
            return 10;
        }

        TICK_MEMBER_REQUIRES(!std::is_same<T, double>::value)
        T foo() {
            return 10;
        }

};

আপনি নিজের সদস্যকেও এর মতো ম্যাক্রো প্রয়োগ করতে পারেন (কেবলমাত্র আপনি অন্য লাইব্রেরিটি ব্যবহার করতে না চাইলে):

template<long N>
struct requires_enum
{
    enum class type
    {
        none,
        all       
    };
};


#define MEMBER_REQUIRES(...) \
typename requires_enum<__LINE__>::type PrivateRequiresEnum ## __LINE__ = requires_enum<__LINE__>::type::none, \
class=typename std::enable_if<((PrivateRequiresEnum ## __LINE__ == requires_enum<__LINE__>::type::none) && (__VA_ARGS__))>::type

এটি আমার পক্ষে সেভাবে কার্যকর হয়নি। মায়েবে কিছু গায়েবি? আপনি কি ওপি উদাহরণটি একটি কার্যকরী আকারে আবার লিখতে পারেন, দয়া করে?
ব্যবহারকারী 1284631

আসল উদাহরণ ওভারলোডিং নিয়ে কাজ করে না। আপনি কীভাবে ওভারলোডিং দিয়ে এটি করতে পারেন তা আমি আমার উত্তর আপডেট করেছি।
পল ফুলটজ দ্বিতীয়

0

এখানে ম্যাক্রো ব্যবহার করে আমার নূন্যতম উদাহরণ। enable_if((...))আরও জটিল এক্সপ্রেশন ব্যবহার করার সময় ডাবল বন্ধনী ব্যবহার করুন।

template<bool b, std::enable_if_t<b, int> = 0>
using helper_enable_if = int;

#define enable_if(value) typename = helper_enable_if<value>

struct Test
{
     template<enable_if(false)>
     void run();
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.