为什么可变参数模板构造函数比复制构造函数更好地匹配? [英] Why variadic template constructor matches better than copy constructor?

查看:106
本文介绍了为什么可变参数模板构造函数比复制构造函数更好地匹配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码无法编译:

#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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆