如何通过const引用或通过转发模板临时构造对象

use*_*708 8 c++ perfect-forwarding type-deduction c++14

考虑这个最小的例子

template <class T>
class Foo
{
public:
    Foo(const T& t_)
        : t(t_)
    {
    }

    Foo(T&& t_)
        : t(std::move(t_))
    {
    }

    T t;
};

template <typename F>
Foo<F> makeFoo(F&& f)
{
    return Foo<F>(std::forward<F>(f));
}

int main()
{
    class C
    {

    };

    C c;

    makeFoo(c);
}
Run Code Online (Sandbox Code Playgroud)

MSVC 2017失败,并出现Foo ctor的重新定义错误。显然,T被推导为C&而不是预期的C。这是如何发生的,以及如何修改代码以使其达到预期的目的:从const引用复制构造Foo :: t或从r-移动构造值。

Bia*_*sta 8

C ++ 17中,您可以简单地编写:

template <typename F>
auto makeFoo(F&& f)
{
    return Foo(std::forward<F>(f));
}
Run Code Online (Sandbox Code Playgroud)

由于类模板参数的推导


C ++ 14中,您可以编写:

template <typename F>
auto makeFoo(F&& f)
{
    return Foo<std::decay_t<F>>(std::forward<F>(f));
}
Run Code Online (Sandbox Code Playgroud)


Yak*_*ont 6

template <class F, class R = std::decay_t<F>>
Foo<R> makeFoo(F&& f)
{
  return Foo<R>(std::forward<F>(f));
}
Run Code Online (Sandbox Code Playgroud)

这是解决问题的一种干净简单的方法。

衰减是将类型转换为适合存储在某处的类型的一种适当方法。它对数组类型做不好的事情,但是在其他方面做得差不多。您的代码无论如何都不适用于数组类型。


编译器错误是由于引用折叠规则引起的。

 X          X&          X const&       X&&
 int        int&        int const&     int&&
 int&       int&        int&           int&
 int const  int const&  int const&     int const&&
 int&&      int&        int&           int&&
 int const& int const&  int const&     int const&
Run Code Online (Sandbox Code Playgroud)

这些看起来很奇怪。

第一条规则是const引用是引用,但是对const的引用是不同的。您不能限定“参考”部分;您只能const限定所引用的部分。

当你有T=int&,当你计算T const或时const T,你就会得到int&

第二部分与如何一起使用r和l值引用有关。当你这样做int& &&int&& &(你不能直接做,而是你做T=int&那么T&&T=int&&T&),你总是一个左参考- T&。左值胜过右值。

然后,我们添加有关如何T&&推导类型的规则;如果传递一个可变的类型的左值C,则进入T=C&对的调用makeFoo

所以你有:

template<F = C&>
Foo<C&> makeFoo( C& && f )
Run Code Online (Sandbox Code Playgroud)

作为您的签名,又名

template<F = C&>
Foo<C&> makeFoo( C& f )
Run Code Online (Sandbox Code Playgroud)

现在我们来看一下Foo<C&>。它有两个ctor:

Foo( C& const& )
Foo( C& && )
Run Code Online (Sandbox Code Playgroud)

对于第一个,const在引用上将被丢弃:

Foo( C& & )
Foo( C& && )
Run Code Online (Sandbox Code Playgroud)

接下来,对引用的引用就是引用,左值引用胜过右值引用:

Foo( C& )
Foo( C& )
Run Code Online (Sandbox Code Playgroud)

然后,我们走了两个相同的签名构造函数。

TL; DR-在此答案的开头进行操作。