其他分享
首页 > 其他分享> > c – 为什么在初始化列表可用时使用可变参数?

c – 为什么在初始化列表可用时使用可变参数?

作者:互联网

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

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

此外,随着参数数量的增加,初始化程序列表似乎具有明显更好的性能.

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

解决方法:

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

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

#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,c11,performance,initializer-list,variadic-functions
来源: https://codeday.me/bug/20190928/1826703.html