Использование enable_if для сопоставления только с классами, которые имеют определенный элемент статических данных и которые имеют только определенные значения - PullRequest
0 голосов
/ 29 сентября 2018

Я хочу специализировать функцию для подмножества классов, которые: имеют определенную статическую переменную-член данных, и такая переменная имеет только определенные возможные значения.

Приведенный ниже код иллюстрирует намерение, но это не таккомпилировать, если я не закомментирую строки, относящиеся к B классам в main.Это связано с тем, что code не является членом классов Bx, но условие enable_if является действительным, если аргумент шаблона содержит code переменных-членов.Как мне изменить его?

К сожалению, я работаю с очень старыми компиляторами, поэтому не поддерживает C ++ 11: я компилирую с опцией -std=c++03.

Спасибо

#include <iostream>
#include <boost/core/enable_if.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/bool.hpp>
using std::cout;
using namespace boost;
using namespace boost::mpl;

template <int N> struct A1 { static const int code = N; };
template <int N> struct A2 { static const int code = N; };
// ... other classes with static data member 'code'
template <int N> struct AN { static const int code = N; };

struct B1{};
struct B2{};
// ... other classes potentially passd as argument to the foo function
struct BN{};

template <typename T>
struct Condition : or_<bool_<T::code == 1>, bool_<T::code == 2> > {};


template <typename T>
typename enable_if<not_<Condition<T> >, void>::type
   foo(const T& arg) { cout << "This class does not have a static member code or its value is not 1 or 2\n"; }

template <typename T>
typename enable_if<Condition<T>, void>::type
   foo(const T& arg) { cout << "This class has a static member code and its value is " << T::code << "\n"; }

int main()
{
    foo(A1<0>()); // this should match the 1st version of foo
    foo(A2<1>()); // this should match the 2nd version of foo
    foo(AN<2>()); // this should match the 2nd version of foo
    foo(B1());    // this should match the 1st version of foo
    foo(BN());    // this should match the 1st version of foo
}

Ответы [ 2 ]

0 голосов
/ 29 сентября 2018

Реальная проблема в том, что вам нужно решение на C ++ 03, поэтому вы можете использовать SFINAE, но не все улучшения языка, доступные начиная с C ++ 11.

В любом случае, я предлагаю вам решениеэто извилистое, как у вас (может быть, больше), но совершенно бесплатное повышение.

Если вы определяете тривиальную оболочку bool (которая может примерно заменить C ++ 11 std::true_type и std::false_type)

template <bool B>
struct bool_wrapper
 { static const bool value = B; };

вы можете определить ваше условие следующим образом

template <typename, typename = bool_wrapper<true> >
struct cond : public bool_wrapper<false>
 { };

template <typename T>
struct cond<T, bool_wrapper<(1 == T::code) || (2 == T::code)> >
   : public bool_wrapper<true>
 { };

и если вы определите черты типа enable_if (такие же, как в C ++ 11 std::enable_if)

template <bool, typename = void>
struct enable_if
 { };

template <typename T>
struct enable_if<true, T>
 { typedef T type; };

вы можете SFINAE включить / отключить ваши foo() функции

template <typename T>
typename enable_if<false == cond<T>::value>::type foo (T const & arg)
 { std::cout << "no static member code or value not 1 and not 2\n"; }

template <typename T>
typename enable_if<true == cond<T>::value>::type foo (T const & arg)
 { std::cout << "static member code and its value is " << T::code << "\n"; }

Ниже приведен полностью рабочий пример C ++ 98

#include <iostream>

template <int N> struct A1 { static const int code = N; };
template <int N> struct A2 { static const int code = N; };
// ... 
template <int N> struct AN { static const int code = N; };

struct B1{};
struct B2{};
// ...
struct BN{};

template <bool B>
struct bool_wrapper
 { static const bool value = B; };

template <typename, typename = bool_wrapper<true> >
struct cond : public bool_wrapper<false>
 { };

template <typename T>
struct cond<T, bool_wrapper<(1 == T::code) || (2 == T::code)> >
   : public bool_wrapper<true>
 { };

template <bool, typename = void>
struct enable_if
 { };

template <typename T>
struct enable_if<true, T>
 { typedef T type; };


template <typename T>
typename enable_if<false == cond<T>::value>::type foo (T const & arg)
 { std::cout << "no static member code or value not 1 and not 2\n"; }

template <typename T>
typename enable_if<true == cond<T>::value>::type foo (T const & arg)
 { std::cout << "static member code and its value is " << T::code << "\n"; }

int main ()
 {
   foo(A1<0>()); // match the 1st version of foo
   foo(A2<1>()); // match the 2nd version of foo
   foo(AN<2>()); // match the 2nd version of foo
   foo(B1());    // match the 1st version of foo
   foo(BN());    // match the 1st version of foo
 }

Я не знаю повышенияклассы, которыми вы пользуетесь, но я полагаю, что вы можете изменить свой код (чтобы он работал почти как мое решение без буста) следующим образом

template <typename, typename = bool_<true> >
struct Condition : public bool_<false>
 { };

template <typename T>
struct Condition<T, bool_<(1 == T::code) || (2 == T::code)> >
   : public bool_<true>
 { };

- РЕДАКТИРОВАТЬ -

ОП спрашивают

Я не понимаю, как это работает для случая A1 <0>.Специализация Condition должна быть предпочтительным совпадением, со вторым аргументом, расширяющимся до bool_.Этот класс наследуется от bool_, поэтому он должен выбирать неправильные версии foo.Однако это работает.Как это возможно?

Хорошо ... когда вы пишете foo(A1<0>()), компилятор должен понимать, является ли cond<A1<0>>::value true или false для включения первой версии foo()или второй.

Таким образом, компилятор должен реализовать cond<A1<0>>.Но нет шаблона класса cond, который получает только имя типа.В любом случае, компилятор обнаружил, что

template <typename, typename = bool_wrapper<true> >
struct cond;

совпадает с использованием значения по умолчанию для второго параметра шаблона.

Альтернативы нет, поэтому двусмысленности нет, поэтому cond< A<1> > становитсяcond< A<1>, bool_wrapper<true> >

Теперь компилятор должен выбирать между основной версией cond<typename, typename> (той, которая наследуется от bool_wrapper<false>) и специализацией (той, которая наследуется от bool_wrapper<true>).

cond< A<1>, bool_wrapper<true> > точно соответствует основной версии, но соответствует также специализации?Если совпадает также со специализацией, компилятор должен предпочесть специализацию.

Так что нам нужно посмотреть, соответствует ли cond< A<0>, bool_wrapper<true> > специализации.

Используя A<0> как T, мы имеемчто специализация становится

cond< A<0>, bool_wrapper<(1 == A<0>::code) || (2 == A<0>::code)> >

, то есть

cond< A<0>, bool_wrapper<(1 == 0) || (2 == 0)> >

, то есть

cond< A<0>, bool_wrapper<false || false> >

, то есть

cond< A<0>, bool_wrapper<false> >

, и это не 't соответствует cond< A<0>, bool_wrapper<true> >.

Так что cond< A<0> >, то есть cond< A<0>, bool_wrapper<true> >, соответствует только основной версии cond<typename, typename>, поэтому наследуем от bool_wrapper<false>.

Теперь мы можем датьcond< A<1> >.

Что касается cond< A<0> >, единственный шаблон cond, который соответствует cond< A<1> >, это cond<typename, typename> со вторым typename со значением по умолчанию.

Итак cond< A<1> > - это cond< A<1>, bool_wrapper<true> >.

Но cond< A<1>, bool_wrapper<true> > соответствует только основной версии cond<typename, typename> или также специализации?

Мы можем видеть, что A<1> используется как T, у нас есть специализация:

cond< A<1>, bool_wrapper<(1 == A<1>::code) || (2 == A<1>::code)> >

, то есть

cond< A<1>, bool_wrapper<(1 == 1) || (2 == 1)> >

, то есть

cond< A<1>, bool_wrapper<true || false> >

, то есть

cond< A<1>, bool_wrapper<true> >

и это соответствует cond< A<1>, bool_wrapper<true> >.

Итак, дляcond< A<1> >, AKA cond< A<1>, bool_wrapper<true>, обе версии cond<typename, typename> совпадают, поэтому компилятор должен выбрать специализацию, поэтому cond< A<1> > наследуется от bool_wrapper<true>.

0 голосов
/ 29 сентября 2018

По предложению Дж. Цвинка я получил его на работу.

Помещение двух условий в шаблонный класс and_ не работает по той же причине, как указано выше.Однако я могу поместить первое условие (класс имеет переменную-член code) в enable_if, а затем второе условие (код имеет определенные значения) внутри класса (см. Пример ниже).

Если естьнемного запутанный, хотя.Если кто-нибудь может предложить более элегантное решение, я приму его.

#include <boost/tti/has_static_member_data.hpp>

BOOST_TTI_HAS_STATIC_MEMBER_DATA(code)

template <typename T, typename Enable = void>
struct Condition : false_type {};;

template <typename T>
struct Condition<T, typename enable_if<bool_<has_static_member_data_code<T,const int>::value> >::type>
{
    typedef or_<bool_<T::code == 1>, bool_<T::code == 2> > type;
    const static bool value = type::value;
};
...