5

考虑以下代码:

#include <iostream>
#include <type_traits>

// Variadic version
template<class... Variadic>
void f(const Variadic&... variadic)
{
    std::cout<<"variadic"<<std::endl;
}

// Single version
template<class Single, class = typename std::enable_if<std::is_fundamental<Single>::value>::type>
void f(const Single& single)
{
    std::cout<<"single"<<std::endl;
}

// Main
int main()
{
    f();              // variadic
    f(42);            // single : why?
    f(std::string()); // variadic 
    f(42, 42);        // variadic
    return 0;
}

我不明白为什么标记为“single”的行编译得很好(在 g++ 4.6.3 下)并且不会产生重载解决问题。c++11 标准是否说具有固定数量参数的模板函数优于具有相同签名的可变参数函数?

4

2 回答 2

5

这真的 很简单(两个活生生的例子,gcc 和 clang)

template<class...T> void foo(T&&...) {std::cout << "...T\n";}
template<class T> void foo(T&&) {std::cout << "T\n";}
int main() {
  foo(3);
}

Overloads not taking ...seem to be preferred when the choice is an explicit template parameter.

class=std::enable_if_t不会改变这一点。

所以你的两个函数f都是候选函数,那么编译器更喜欢没有变量的函数。

14.8.2.4 在部分排序期间推导模板参数 [temp.deduct.partial]

/8:

如果A是从函数参数包转换而来的并且P不是参数包,则类型推导失败。否则,使用结果类型PA,然后按 中所述进行推导14.8.2.5。如果P是函数形参包,则将实A参模板的每个剩余形参类型的类型与函数形参包的 declarator-id 的类型 P 进行比较。每个比较推导出模板参数包中由函数参数包扩展的后续位置的模板参数。如果给定类型的推导成功,则参数模板中的类型被认为至少与参数模板中的类型一样特化。[ 例子:

template<class... Args> void f(Args... args); // #1
template<class T1, class... Args> void f(T1 a1, Args... args); // #2
template<class T1, class T2> void f(T1 a1, T2 a2); // #3
f(); // calls #1
f(1, 2, 3); // calls #2
f(1, 2); // calls #3; non-variadic template #3 is more
// specialized than the variadic templates #1 and #

特别是f(1,2)例子。

当您传递 a as时,该enable_if_t子句所做的只是从考虑中删除单参数版本。std::stringT

于 2014-09-11T14:17:51.063 回答
2

由于在“单一”版本中使用了第二个模板参数 enable_if,编译器认为该版本是一个更专业的模板,可与启用它的类型一起使用。
它被认为更专业,因为有些类型可以实例化可变参数模板,但“单个”不能。

一般规则是更专业的模板在重载决议中胜过不那么专业的模板。

于 2013-01-06T09:07:17.497 回答