为什么可变参数函数不能与模板一起使用

时间:2018-10-09 13:02:37

标签: c++ templates variadic-templates variadic

我想编写一个比较多个值并检查其中哪个值最小的函数。我想将其实现为可变参数函数模板。我知道自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 :(得分:3)

T smallestOf(const int count, const T val1, ... )

当您这样调用时:smallestOf(3, 10, 20, 30)的变量是20 30(因为10val1)。因此,您需要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)); }

您可能会想说这不是最理想的,或者可以将调用分组以便减少调用,但是任何不错的编译器都会内联并为您优化所有这些调用。 clanggcc都只用无分支的min<int,....> movcmp指令编译具有10个参数的cmov

答案 1 :(得分:0)

仅注意到注释中没有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