转发参数时应该使用()或{}吗
Should I use () or {} when forwarding arguments?
我有以下类:
struct foo
{
std::size_t _size;
int* data;
public:
explicit foo(std::size_t s) : _size(s) { }
foo(std::size_t s, int v)
: _size(s)
{
data = new int[_size];
std::fill(&data[0], &data[0] + _size, v);
}
foo(std::initializer_list<int> d)
: _size(d.size())
{
data = new int[_size];
std::copy(d.begin(), d.end(), &data[0]);
}
~foo() { delete[] data; }
std::size_t size() const { return _size; }
};
我想提出这样的论点:
template <typename... Args>
auto forward_args(Args&&... args)
{
return foo{std::forward<Args>(args)...}.size();
//--------^---------------------------^
}
std::cout << forward_args(1, 2) << " " << forward_args(1) << " "
<< forward_args(2) << "n";
如果我用()
替换{}
,则输出为1 1 2
而不是2 1 1
。
哪一个对我的课最有意义?
在这种情况下,因为类有一个采用std::initializer_list
的构造函数,所以在工厂函数中使用{}
通常会改变传入的任何参数列表的语义,方法是将任何长于2的参数包变成initializer_list。
这将使该功能的用户感到惊讶。
因此,使用()
表单。想要传递initializer_list的用户可以通过调用来显式传递
forward_args({ ... });
注意,要使上述语法正常工作,您需要提供另一个重载:
template <class T>
auto forward_args(std::initializer_list<T> li)
{
return foo(li).size();
}
{}
与()
的使用决定了调用哪个构造函数。
-
{}
将调用表单foo(std::initializer_list<int> d)
,您将得到您所期望的结果。 -
()
将调用explicit foo(std::size_t s)
和foo(std::size_t s, int v)
,在所有情况下,第一个元素都是大小,所以给定参数,就会得到您看到的结果。
支持哪种形式取决于您希望forward_args
方法支持什么语义。如果希望参数按原样传递,则应使用()
(在这种情况下,用户需要首先提供initialiser_list
作为参数)。
可能(可能)您希望使用的形式是()
,使用如下;
template <typename... Args>
auto forward_args(Args&&... args)
{
return foo(std::forward<Args>(args)...).size();
//--------^---------------------------^
}
int main()
{
std::cout << forward_args(std::initializer_list<int>{1, 2}) << " "
<< forward_args(std::initializer_list<int>{3}) << " "
<< forward_args(std::initializer_list<int>{4}) << "n";
}
旁注;CCD_ 19上的cppreference页面就是这种行为的一个很好的例子。
如果需要(即支持forward_args({1,2})
),您可以为initializer_list
提供过载;
template <class Arg>
auto forward_args(std::initializer_list<Arg> arg)
{
return foo(std::move(arg)).size();
}
如前所述:给定initializer_list
,类构造函数最终是混乱的根源,这在对象构造过程中表现出来。CCD_ 23与CCD_;后者调用构造函数的CCD_ 25形式。解决这个问题的一种技术是使用"标记"来区分"普通"构造函数形式和initializer_list
形式。
实际上,您不应该这样定义类foo
(前提是它在您的控制范围内)。这两个构造函数有重叠,并导致两个初始化的糟糕情况:
foo f1(3);
foo f2{3};
意思是两种不同的东西;并且需要编写转发函数的人面临无法解决的问题。这在本博客文章中有详细介绍。CCD_ 28也存在同样的问题。
相反,我建议您使用一个"标记"构造函数:
struct with_size {}; // just a tag
struct foo
{
explicit foo(with_size, std::size_t s);
explicit foo(with_size, std::size_t s, int v);
foo(std::initializer_list<int> d);
// ...
}
现在您没有重叠,并且可以安全地使用{}变体。
- 是否可以在不扣除的情况下将模板参数转发到 make_*?
- 可变参数模板参数转发使用逗号运算符
- 如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?
- 将可变参数函数参数转发到 std::function 对象
- 我如何实现变异模式以将可变数量的参数转发到C 11中的函数
- 无临时实例的参数转发
- 如何将构造函数参数转发到 boost::optional
- 如何使包装类将其构造函数参数转发到 std::vector 的构造函数?
- 如何将 CMake 参数转发到外部项目
- 使用std::forward而不是将参数转发到另一个函数
- 使用declard和参数转发构造函数隐式复制结构
- 将可变参数模板参数转发到多个类成员
- Template()的参数转发使构造函数参数变为常量
- 我怎样才能完美地将参数转发到 STL 集合
- 如何在 C++11 中使用可变参数模板正确创建显式参数转发函数
- 使用默认参数转发引用
- 将可变模板参数转发到类似printf的函数
- static_cast的参数转发
- Boost.Program_options:将"--"后面的参数转发到另一个程序
- 将参数转发给模板成员函数