为什么可变参数模板构造函数比复制构造函数更好地匹配? [英] Why variadic template constructor matches better than copy constructor?
问题描述
以下代码无法编译:
#include <iostream>
#include <utility>
struct Foo
{
Foo() { std::cout << "Foo()" << std::endl; }
Foo(int) { std::cout << "Foo(int)" << std::endl; }
};
template <typename T>
struct Bar
{
Foo foo;
Bar(const Bar&) { std::cout << "Bar(const Bar&)" << std::endl; }
template <typename... Args>
Bar(Args&&... args) : foo(std::forward<Args>(args)...)
{
std::cout << "Bar(Args&&... args)" << std::endl;
}
};
int main()
{
Bar<Foo> bar1{};
Bar<Foo> bar2{bar1};
}
编译器错误提示我,编译器正在尝试使用可变参数模板构造函数而不是复制构造函数:
Compiler error suggest to me that compiler was trying to use variadic template constructor instead of copy constructor:
prog.cpp: In instantiation of 'Bar<T>::Bar(Args&& ...) [with Args = {Bar<Foo>&}; T = Foo]':
prog.cpp:27:20: required from here
prog.cpp:18:55: error: no matching function for call to 'Foo::Foo(Bar<Foo>&)'
Bar(Args&&... args) : foo(std::forward<Args>(args)...)
为什么编译器会这样做以及如何解决?
Why compiler does that and how to fix it?
推荐答案
此呼叫:
Bar<Foo> bar2{bar1};
在其重载集中有两个候选项:
has two candidates in its overload set:
Bar(const Bar&);
Bar(Bar&); // Args... = {Bar&}
确定是否一个转换序列的方法之一优于另一个,来自[over.ics.rank]:
One of the ways to determine if one conversion sequence is better than the other is, from [over.ics.rank]:
标准转换顺序S1比标准转换更好序列
S2如果
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
— [...]
— S1和S2是引用绑定(8.5.3),并且除顶级 cv 限定符外,引用所引用的类型与
类型相同,并且由S2初始化的引用所引用的类型
更多。 cv 限定,而不是由S1初始化的引用所引用的类型。 [示例:
— [...]
— S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same
type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers
is more cv-qualified than the type to which the reference initialized by S1 refers. [ Example:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)
int k = g(i); // ambiguous
-结束示例]
转发引用可变参数构造函数是更好的匹配,因为其引用绑定( Bar&
)小于 cv 胜过复制构造函数的引用绑定( const Bar&
)。
The forwarding reference variadic constructor is a better match because its reference binding (Bar&
) is less cv-qualified than the copy constructor's reference binding (const Bar&
).
作为解决方案,您可以随时将 Args ...
排除在候选集之外,您应该使用SFINAE调用副本或移动构造函数:
As far as solutions, you could simply exclude from the candidate set anytime Args...
is something that you should call the copy or move constructor with SFINAE:
template <typename... > struct typelist;
template <typename... Args,
typename = std::enable_if_t<
!std::is_same<typelist<Bar>,
typelist<std::decay_t<Args>...>>::value
>>
Bar(Args&&... args)
如果 Args ...
是 Bar
, Bar&
,<$ c $之一c> Bar& , const Bar&
,然后 typelist< decay_t< Args ......>
将是 typelist< Bar>
-这是我们要排除的情况。其他任何 Args ...
组也可以。
If Args...
is one of Bar
, Bar&
, Bar&&
, const Bar&
, then typelist<decay_t<Args>...>
will be typelist<Bar>
- and that's a case we want to exclude. Any other set of Args...
will be allowed just fine.
这篇关于为什么可变参数模板构造函数比复制构造函数更好地匹配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!