C++ 为什么可变模板构造函数比复制构造函数更匹配?

C++ 为什么可变模板构造函数比复制构造函数更匹配?,c++,templates,c++11,variadic-templates,overload-resolution,C++,Templates,C++11,Variadic Templates,Overload Resolution,以下代码未编译: #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 {

以下代码未编译:

#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};
}
#包括
#包括
结构Foo
{

Foo(){std::cout虽然我同意这是违反直觉的,但原因是您的复制构造函数需要一个
常量条&
,但是
bar1
不是常量

由于通用引用可以绑定任何内容,因此它是在更严格的构造函数上选择的,并满足常量要求。

此调用:

Bar<Foo> bar2{bar1};
确定一个转换序列是否优于另一个转换序列的方法之一是,从[over.ics.rank]:

标准转换序列S1是比标准转换序列更好的转换序列 S2如果

-[…]
-S1和S2是引用绑定(8.5.3),引用引用的类型相同 除顶级cv限定符外的类型,以及由S2初始化的引用所引用的类型 比由S1初始化的引用引用引用的类型更符合cv条件。[示例:

-[结束示例]

转发引用变量构造函数是一个更好的匹配,因为它的引用绑定(
Bar&
)比复制构造函数的引用绑定(
const Bar&
)的cv限定更少

就解决方案而言,您可以随时从候选集合中排除。
Args…
是您应该使用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&&…Args)

如果
Args…
Bar
Bar&
Bar&
const Bar&
中的一种,那么
typelist
将是
typelist
-这是我们想要排除的情况。任何其他
Args…
集都可以很好地使用。

另一种避免选择可变构造函数的方法是提供所有形式的
Bar
构造函数

这需要更多的工作,但避免了启用的复杂性,如果,如果这对您很重要:

#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; }
    Bar(Bar&) { std::cout << "Bar(Bar&)" << std::endl; }
    Bar(Bar&&) { std::cout << "Bar(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};
}
#包括
#包括
结构Foo
{

Foo(){std::cout构造函数模板可以推断出对非常量
Bar
的左值引用,这比复制构造函数更匹配。因为通用构造函数需要更少的转换(即无)。可能与C++11版本重复:template::type>@kpx1894否,C++11版本首先在您自己的命名空间中写入
Decation\t
enable\t,如果
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
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)
#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; }
    Bar(Bar&) { std::cout << "Bar(Bar&)" << std::endl; }
    Bar(Bar&&) { std::cout << "Bar(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};
}