为什么模板构造函数比复制构造函数更受青睐?

ant*_*_rh 33 c++ copy-constructor constructor-overloading function-templates overload-resolution

#include <iostream>

struct uct
{
    uct() { std::cerr << "default" << std::endl; }

    uct(const uct &) { std::cerr << "copy" << std::endl; }
    uct(      uct&&) { std::cerr << "move" << std::endl; }

    uct(const int  &) { std::cerr << "int" << std::endl; }
    uct(      int &&) { std::cerr << "int" << std::endl; }

    template <typename T>
    uct(T &&) { std::cerr << "template" << std::endl; }
};

int main()
{
    uct u1    ; // default
    uct u2( 5); // int
    uct u3(u1); // template, why?
}
Run Code Online (Sandbox Code Playgroud)

大肠杆菌

构造函数的模板重载适用于两个声明(u2u3)。但是,当int传递给构造函数时,将选择非模板重载。调用复制构造函数时,将选择模板重载。据我所知,在重载解析期间,非模板函数总是比模板函数更受青睐。为什么复制构造函数的处理方式不同?

Nat*_*ica 33

据我所知,在重载解析期间,非模板功能总是比模板功能更受青睐。

仅当专业化和非模板完全相同时,这才是正确的。但是这里不是这种情况。当您调用uct u3(u1)过载集

uct(const uct &)
uct(uct &) // from the template
Run Code Online (Sandbox Code Playgroud)

现在,由于u1不是const,因此必须应用const转换来调用复制构造函数。要调用模板专业化,它是完全匹配的,因此无需执行任何操作。这意味着模板将胜出,因为它是更好的匹配。

要停止此操作,您可以使用SFINAE将模板函数限制为仅在T不是时才调用uct。看起来像

template <typename T, std::enable_if_t<!std::is_same_v<uct, std::decay_t<T>>, bool> = true>
uct(T &&) { std::cerr << "template" << std::endl; }
Run Code Online (Sandbox Code Playgroud)

  • 添加const将正向引用转换为右值引用。因此,由于非模板与模板规则的缘故,它不是首选,而是因为uct u3(u1)根本不匹配 (4认同)
  • 只是要补充一点,这就是为什么模板化构造函数是怪兽的原因,并且除非您希望它们超越一切,否则您可能想要标记构造函数。(类似于`inplace_t`) (2认同)
  • 重载* any *函数会导致转发参考。 (2认同)

And*_* DM 5

当试图调用复制构造函数时,选择模板重载。据我所知,在重载解析期间,非模板函数总是比模板函数更受欢迎。为什么复制构造函数的处理方式不同?

template <typename T>
uct(T &&) { std::cerr << "template" << std::endl; }
//    ^^
Run Code Online (Sandbox Code Playgroud)

选择模板化版本的原因是因为编译器能够
生成具有(T &)更适合的签名的构造函数,因此被选择。

  • 如果您将签名从 更改为uct u1const uct u1那么它将适合复制构造函数(因为u1不是 const 开头)。

  • 如果您将签名从 更改为uct(const uct &)uct(uct&)它会更合适,它会选择模板版本。

  • 此外,uct(uct&&)如果您使用过,则会选择uct u3(std::move(u1));


要解决此问题,您可以使用 SFINAE 在T与以下情况相同时禁用过载uct

template <typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, uct>>>
uct(T&&)
{
  std::cerr << "template" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)