Почему функции variadic не работают с шаблонами - PullRequest
0 голосов
/ 09 октября 2018

Я хотел написать функцию, которая сравнивает несколько значений и проверяет, какое из них наименьшее.Я хотел реализовать это как шаблон функции с переменным числом аргументов.Я знаю, что есть возможность для этой цели начиная с C ++ 11, но по причинам, которые я не могу использовать в данный момент.Поэтому я попытался использовать библиотеку <cstdarg>, но наткнулся на некоторые проблемы.По какой-то причине смещения аргументов вычисляются неправильно, когда я использую функцию в качестве шаблона.Если я реализую функцию явно с фиксированным типом, у меня нет проблем.

Мой код:

#include <cstdarg>
#include <iostream>
#include <limits>


template <typename T>
T smallestOf(const int count, const T val1, ... ) { /* I use val1 to determine the template type */
    va_list args;
    va_start(args, val1);
    T smallestVal = std::numeric_limits<T>::max();
    for(int i = 0; i < count; i++) {
        T nextVal = va_arg(args, T);
        std::cout << "nextVal: " << nextVal << std::endl;
        if(nextVal < smallestVal) smallestVal = nextVal;
    }
    va_end(args);
    return smallestVal;
}


int main() {
    std::cout << "Smallest value: " << smallestOf(3, 10, 20, 30) << std::endl;
}

, который выдает следующий вывод:

nextVal: 20
nextVal: 30
nextVal: 4217000
Smallest value: 20

, который выглядит какфункция читает за пределы своей памяти, так как смещения неверны.Почему это так?

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

Только что заметил комментарий о том, что c ++ 11 недоступен, но, поскольку я только что написал его, вот пример вариационного решения, написанного на c ++ 17.

Возможно, это будетпригодится в будущем.

#include <iostream>
#include <utility>
#include <type_traits>

template<class T, class...Rest>
auto smallestOf(T const& val1, Rest const&...rest) 
-> std::enable_if_t<std::is_same_v<std::common_type_t<T, Rest...>, T>, T const&>
{
    auto* current = std::addressof(val1);

    if constexpr (sizeof...(Rest) > 0)
    {
        auto check = [](T const* x, T const* y)
        {
            return std::addressof(std::min(*x, *y));
        };

        ((current = check(current, std::addressof(rest))),...);
    }    
    return *current;
}

int main() {
    std::cout << "Smallest value: " << smallestOf(10) << std::endl;
    std::cout << "Smallest value: " << smallestOf(20, 10) << std::endl;
    std::cout << "Smallest value: " << smallestOf(30, 10, 20) << std::endl;
    std::cout << "Smallest value: " << smallestOf(30, 10, 40, 20) << std::endl;
}

http://coliru.stacked -crooked.com / a / 600a91f1678763b2

0 голосов
/ 09 октября 2018
T smallestOf(const int count, const T val1, ... )

когда вы звоните так: smallestOf(3, 10, 20, 30) varargs 20 30 (потому что 10 равно val1).Поэтому вам нужно count - 1.

Также strong рекомендация: не используйте varargs.Используйте шаблоны с переменным числом аргументов или std::initializer_list

Вы говорите, что не имеете доступа к C ++ 11, поэтому, к сожалению, не имеете доступа ни к шаблонам с переменным числом аргументов, ни к спискам инициализаторов.

Что ж, здесьмой подарок для вас:

template <class T> T min(T e1) { return e1; }
template <class T> T min(T e1, T e2) { return e1 < e2 ? e1: e2; }
template <class T> T min(T e1, T e2, T e3) { return min(e1, min(e2, e3)); }
template <class T> T min(T e1, T e2, T e3, T e4) { return min(e1, min(e2, e3, e4)); }
template <class T> T min(T e1, T e2, T e3, T e4, T e5) { return min(e1, min(e2, e3, e4, e5)); }
template <class T> T min(T e1, T e2, T e3, T e4, T e5, T e6) { return min(e1, min(e2, e3, e4, e5, e6)); }
template <class T> T min(T e1, T e2, T e3, T e4, T e5, T e6, T e7) { return min(e1, min(e2, e3, e4, e5, e6, e7)); }
template <class T> T min(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8) { return min(e1, min(e2, e3, e4, e5, e6, e7, e8)); }
template <class T> T min(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9) { return min(e1, min(e2, e3, e4, e5, e6, e7, e8, e9)); }
template <class T> T min(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9, T e10) { return min(e1, min(e2, e3, e4, e5, e6, e7, e8, e9, e10)); }

Вы можете испытать искушение сказать, что это неоптимально или вы можете сгруппировать вызовы так, чтобы было меньше вызовов, но любой достойный компилятор встроит и оптимизирует все эти вызовы длявы.И clang, и gcc компилируют min<int,....> с 10 параметрами только с инструкциями mov cmp и cmov.

...