c++ - 当初始化列表可用时,为什么现在使用可变参数?

标签 c++ performance c++11 variadic-functions initializer-list

我一直想知道可变参数与初始化列表相比有什么优势。两者都提供相同的功能 - 将无限数量的参数传递给函数。

我个人认为初始化列表更优雅一些。语法不那么尴尬。

此外,随着参数数量的增加,初始化器列表的性能似乎要好得多。

除了在 C 中使用可变参数的可能性之外,我还缺少什么?

最佳答案

如果可变参数是指省略号(如 void foo(...) 中),那么 可变模板 而不是初始化列表或多或少会过时 - 仍然可能有一些用例用于使用 SFINAE 实现(例如)类型特征或 C 兼容性时的省略号,但我将在这里讨论普通用例。

实际上,可变参数模板允许参数包的不同类型(实际上,any 类型),而初始化列表的值必须可转换为初始化列表的基础类型(并且不允许缩小转换):

#include <utility>

template<typename... Ts>
void foo(Ts...) { }

template<typename T>
void bar(std::initializer_list<T>) { }

int main()
{
    foo("Hello World!", 3.14, 42); // OK
    bar({"Hello World!", 3.14, 42}); // ERROR! Cannot deduce T
}

因此,当需要类型推导时,初始化列表很少使用,除非参数的类型确实是同质的。另一方面,可变参数模板提供椭圆可变参数列表的类型安全版本。

此外,调用带有初始化列表的函数需要将参数括在一对大括号中,而对于带有可变参数包的函数则不然。

最后(嗯,还有其他差异,但这些与您的问题更相关),初始化列表中的值是 const对象。根据 C++11 标准的第 18.9/1 段:

An object of type initializer_list<E> provides access to an array of objects of type const E. [...] Copying an initializer list does not copy the underlying elements. [...]

这意味着虽然不可复制的类型可以被移入初始化列表,但它们不能被移出。这个限制可能会也可能不会满足程序的要求,但通常会使初始化列表成为保存不可复制类型的限制选择。

更一般地说,无论如何,当使用一个对象作为初始化列表的元素时,我们要么复制它(如果它是左值),要么远离它(如果它是右值):

#include <utility>
#include <iostream>

struct X
{
    X() { }
    X(X const &x) { std::cout << "X(const&)" << std::endl; }
    X(X&&) { std::cout << "X(X&&)" << std::endl; }
};

void foo(std::initializer_list<X> const& l) { }

int main()
{
    X x, y, z, w;
    foo({x, y, z, std::move(w)}); // Will print "X(X const&)" three times
                                  // and "X(X&&)" once
}

换句话说,初始化列表不能用于通过引用(*)传递参数,更不用说执行完美转发了:

template<typename... Ts>
void bar(Ts&&... args)
{
    std::cout << "bar(Ts&&...)" << std::endl;
    // Possibly do perfect forwarding here and pass the
    // arguments to another function...
}

int main()
{
    X x, y, z, w;
    bar(x, y, z, std::move(w)); // Will only print "bar(Ts&&...)"
}

(*) 但是必须注意,initializer lists (unlike all other containers of the C++ Standard Library) do have reference semantics ,因此尽管在将元素插入初始化器列表时执行了元素的复制/移动,但复制初始化器列表本身不会导致所包含对象的任何复制/移动(如上面引用的标准段落中所述):

int main()
{
    X x, y, z, w;
    auto l1 = {x, y, z, std::move(w)}; // Will print "X(X const&)" three times
                                       // and "X(X&&)" once

    auto l2 = l1; // Will print nothing
}

关于c++ - 当初始化列表可用时,为什么现在使用可变参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15465543/

相关文章:

清除二叉搜索树时的 C++ 段错误

performance - 将元组有效地处理为固定大小的向量

c# - Oracle ODP.NET 托管驱动程序在 64 位中的运行速度比在 32 位中慢 50-100%

performance - 为什么在 MATLAB 中缓存答案需要更长的时间?

c++ - 使用 constexpr 时出现编译错误

c++ - 使用 C++11 进行转换和累加

c++ - g++ 4.7.2 中缺少 std::stoi?

c++ - 错误 : Range-based 'for' loops are not allowed in C++98 mode

c++ - 使用 MSVC 的可变参数函数模板特化中的错误?

c++ - uint8_t VS uint32_t 不同的行为