C ++ STL type_traits вопрос - PullRequest
       6

C ++ STL type_traits вопрос

3 голосов
/ 30 декабря 2010

Я смотрел последнюю C9 лекцию и заметил кое-что интересное ..

В своем введении в type_traits Стефан использует следующий (по его словам, надуманный) пример:

<code>template <code><typename T></code> void foo(T t, true_type)
{
    std::cout << t << " is integral";
}
template <code><typename T></code> void foo(T t, false_type)
{
    std::cout << t << " is not integral";
}</p>

<p>template <code><typename T></code> void bar(T t)
{
    foo(t, typename <code>is_integral<T>::type()</code>);
}

Это кажется намного сложнее, чем:

<code>template <code><typename T></code> void foo(T t)
{
    if(<code>std::is_integral<T>::value</code>)
        std::cout << "integral";
    else
        std::cout << "not integral";
}

Что-то не так с последним способом сделать это? Его путь лучше? Почему?

Спасибо.

Ответы [ 4 ]

14 голосов
/ 30 декабря 2010

В основном первый вариант использует знания об «целостности» типа во время компиляции, а второй вариант - перемещает эти знания во время выполнения.

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

9 голосов
/ 30 декабря 2010

Пример ниже должен иллюстрировать разницу. Давайте добавим struct X:

struct X
{
  X(int)
  {
  }
};

и измените один фу, как показано ниже:

template <typename T> void foo(T t, true_type)
{
    std::cout << t << " is integral";
    X x(t);
}
template <typename T> void foo(T t, false_type)
{
    std::cout << t << " is not integral";
}

Тогда:

template <typename T> void bar(T t)
{
    foo(t, typename is_integral<T>::type());
}

Будет по-прежнему компилироваться для всех типов T (включая целочисленные типы; это может вызвать предупреждение, но будет компилироваться).

Другой эквивалентный код:

template <typename T> void foo(T t)
{
    if(std::is_integral<T>::value)
    {
        std::cout << "integral";
        X x(t);
    }
    else
        std::cout << "not integral";
}

часто не будет компилироваться, так как вы не сможете создать экземпляр X для типов, отличных от целочисленных и, в конечном итоге, классов с плавающей запятой и пользовательских классов, которые имеют оператор int () (оператор short () или оператор long () не подойдут) .

1 голос
/ 30 декабря 2010

Для такого маленького, надуманного примера, нет большого преимущества делать это первым способом.Преимущество приходит, когда у вас более сложные ситуации.По сути, это аналогично использованию полиморфизма на основе наследования или операторов if / switch в объектно-ориентированном программировании.Более сложное решение обеспечивает большую гибкость;Вы можете легко добавлять типы без изменения существующего кода.

Если вы знаете все типы, которые вам когда-либо понадобятся (например, вы используете логическое значение, как пример), то более простое решение может быть лучше.Но если у вас нет фиксированных требований (и когда требования когда-либо будут установлены?), То более сложное, но более гибкое решение, вероятно, будет проще в долгосрочной перспективе.

0 голосов
/ 30 декабря 2010

Используя первый подход, вы можете реализовать статическую диспетчеризацию без , используя if/else или switch.

template <typename T> 
void Dispatch(T t)
{
    //Call foo(T, true_type) or foo(T, false_type)
    //depending upon the *type* of second parameter.
    foo(t, typename is_integral<T>::type());
}

Используя второй подход, вы должны реализовать это, используя блок if/else или switch,

template <typename T> 
void Dispatch(T t)
{
    //Call foo(T, true_type) or foo(T, false_type)
    //depending upon the *value* of value.
    if(std::is_integral<T>::value)
        foo(t, true_type());
    else
        foo(t, false_type());
}

Но если вы хотите реализовать свою функцию Dispatch() безиспользуя if/else, и в то же время вы хотите использовать std::is_integral<T>::value, тогда вам придется переписать вашу foo() функцию, например,

template <bool b> 
void foo(T t)
{
    std::cout << t << " is integral";
}

template <> 
void foo<false>(T t)
{
    std::cout << t << " is not integral";
}

И ваша Dispatch() функция будетвыглядеть,

 template <typename T> 
 void Dispatch(T t)
 {
     //using std::is_integral<T>::value!
     const bool selector = (bool) std::is_integral<T>::value;
     foo<selector>(t);
 }
...