不知大家是否遇到过这样的代码:

template <class... Args>
void f(Args......) {}

参数包后面紧跟着 6 个 .,一般不是用 ... 来扩展参数包吗?这是什么鬼东西?

这个其实可以称为 Two ellipsis operators(非正式称谓),虽说极其罕见,但却是 C++11 标准的一部分,见 [dcl.fct]/3:

parameter-declaration-clause:
 parameter-declaration-listopt …opt
 parameter-declaration-list , …

也就是说,它其实是 ..., ... 的省略写法。以下这三种写法完全等价:

template <class... Args> void f(Args......) {}
template <class... Args> void f(Args... ...) {}
template <class... Args> void f(Args..., ...) {}

...... 是最简洁的写法,多为使用。那么它有什么作用呢?

这又是向后兼容 C 的产物,第一个 ... 用于扩展 C++ 可变模板参数包,第二个 ... 则用于匹配 C Variadic functions[1] 中的旧式变参。

主要目的就在于识别 C Variadic functions,举个例子:

template <class... Args>
void f(void (*fp)(Args...)) {
    std::cout << "#1\n";
}

template <class... Args>
void f(void (*fp)(Args......)) {
    std::cout << "#2\n";
}

void g(int, float) {}
void h(int, ...) {}

int main() {
    f(&g); // calls #1
    f(&h); // calls #2
}

早期 std::is_function[2] 的实现就使用这一手法来识别 C Variadic functions。

// primary template
template<class>
struct is_function : std::false_type {};

// specialization for regular functions
template<class Ret, class... Args>
struct is_function<Ret(Args...)> : std::true_type {};

// specialization for variadic functions such as std::printf
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};

// ...

因此,它才能识别 printf/fprintf 这些旧式可变函数。不过,早期这种实现太过麻烦,如今利用概念直接求解,变得极为简单:

template<class T>
struct is_function : std::integral_constant<
    bool,
    !std::is_const<const T>::value && !std::is_reference<T>::value
> {};

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use the Markdown in the comment form.