C ++ проблема вывода типа шаблона - PullRequest
4 голосов
/ 06 апреля 2010

мотивация: я хотел бы создать служебный класс, чтобы вместо необходимости писать:

if( someVal ==  val1 || someVal == val2 || someVal == val3 )

Я мог бы вместо этого написать:

if( is(someVal).in(val1, val2, val3) )

, который намного ближе к математическому «a является элементом (b, c, d)», а также сэкономил бы много печатания, когда имя переменной someVal длинное.

Вот код, который у меня есть (для 2 и 3 значений):

template<class T>
class is {
private:
    T t_;
public:
    is(T t) : t_(t) { }

    bool in(const T& v1, const T& v2) { 
        return t_ == v1 || t_ == v2; 
    }
    bool in(const T& v1, const T& v2, const T& v3) { 
        return t_ == v1 || t_ == v2 || t_ == v3; 
    }
};

Однако он не скомпилируется, если я напишу:

is(1).in(3,4,5);

вместо этого я должен написать

is<int>(1).in(3,4,5);

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

Ответы [ 6 ]

14 голосов
/ 06 апреля 2010

Если вы хотите сохранить этот синтаксис, вы можете использовать вспомогательную функцию, например:

template<class T>
class is_op {
private:
    T t_;
public:
    is_op(T t) : t_(t) { }

    bool in(const T& v1, const T& v2) { 
        return t_ == v1 || t_ == v2; 
    }
    bool in(const T& v1, const T& v2, const T& v3) { 
        return t_ == v1 || t_ == v2 || t_ == v3; 
    }
};


template< class U >
inline is_op<U> is( U const& v )
{
    return is_op<U>( v );
}

int main(int argc, char* argv[])
{
    is( 1 ).in( 1 , 2 , 4 );
}
5 голосов
/ 06 апреля 2010

Проблема довольно забавная, это правда, что булевы условия могут стать волосатыми.

Хотя я сам предпочитаю писать специальные функции, потому что значение здесь трудно передать. Что значит:

if (someVal == val1 || someVal == val2 || someVal == val3)

значит?

Есть

if ( is(someval).in(val1, val2, val3) )
// or
if ( is(someval).in(val1)(val2)(val3) ) // implements short-circuiting
                                        // and removes arity issue
                                        // using a proxy object

лучше

Я думаю, было бы легче читать:

bool isToBeLogged(const Foo& foo)
{
  // Either
  static std::set<Foo> ValuesToLog = /* some boost assign magic or whatever */;
  return ValuesToLog.find(foo) != ValuesToLog.end();

  // Or
  return foo == val1 || foo == val2 || foo == val3;
}


if (isToBeLogged(someVal))

Полагаю, это вопрос стиля.

Преимущества второго метода, в том числе:

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

Недостаточно? Я думаю, это больше печатать ... о хорошо: p

1 голос
/ 06 апреля 2010

Что касается общей проблемы, может пригодиться такая служебная функция, как содержит:

#include <boost/range.hpp>
template <class Range, class T>
bool contains(const Range& range, const T& value)
{
    return std::find(boost::begin(range), boost::end(range), value) != boost::end(range);
}

(Использование Boost позволяет также принимать массивы, хотя эту перегрузку можно записать отдельно. Это также может быть перегружено для контейнеров с функцией find member.)

В C ++ 0x это может быть расширено для поддержки std::initializer_list<T>*, что позволяет довольно приятно использовать:

if (contains({1, 2, 3}, value) {...}

* Не уверен, что он уже не должен работать, но мой компилятор требует перегрузки, чтобы заставить его работать.

1 голос
/ 06 апреля 2010
template<typename T>
is<T> is_value(T value)
{
    return is<T>(value);
}

int main()
{
    bool r ;

    r = is_value(1).in(3,4,5);
    r = is_value(3).in(3,4,5);

    return 0;
}
0 голосов
/ 06 апреля 2010

модификацией для вашего класса сравнения может быть использование varargs, чтобы сделать его общим для n элементов набора.

0 голосов
/ 06 апреля 2010

Вы застряли с ним - конструктор требует, чтобы тип был указан в качестве параметра шаблона. И я должен заметить, что мне действительно не нравится твоя идея (особенно название класса). Почему бы не использовать набор std:;? Или даже шаблонная функция - что-то вроде:

template <typename  T>
bool IsIn( T v, T a, T b, T c ) {
   ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...