构造函数中的完美转发(C ++ 17)

时间:2018-11-28 12:39:22

标签: c++ gcc language-lawyer c++17 template-deduction

考虑以下代码

struct A {
    A(int id) : id_ { id } {}

    A(const A& rhs) { std::cout << "cctor from " +
        std::to_string(rhs.id_) << std::endl; }
    A(A&& rhs) { std::cout << "mctor from " +
        std::to_string(rhs.id_) << std::endl; }

    int id_;
};

template<typename T>
struct B1 {
    constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}

    T x_;
};

template<typename T>
struct B2 {
    constexpr B2(T&& x) noexcept;

    T x_;
};

template<typename T>
constexpr
B2<T>::B2(
    T&& x
) noexcept :
    x_ { std::forward<T>(x) } {
}

int
main(
) {
    A a { 1 };

    //B1 b11 { a }; // Not compiling
    B1 b12 { A { 2 } };

    B2 b21 { a };
    B2 b22 { A { 3 } };

    return 0;
 }

产生

mctor from 2
mctor from 3

因此,从根本上来说,外部定义的构造函数似乎完美地转发了其参数的值类别,而内联定义的构造函数则没有。

是像函数模板(完美转发其参数)那样处理外部定义的构造函数吗?

欢迎链接到标准的相应部分。

我正在使用GCC 7.2.0。

2 个答案:

答案 0 :(得分:9)

这是一个GCC错误。转发引用具有非常明确的定义:

  

[临时扣除额] (强调我的意思)

     

3 转发引用是对   不代表模板的cv不合格模板参数   类模板的参数(在类模板参数期间   扣除([over.match.class.deduct]))。如果P是转发参考   并且参数是左值,类型“对A的左值引用”是   代替A进行类型推导。

在两种情况下,T在CTAD期间都为封闭类指定了一个模板参数,因此无论哪种方式都不应产生转发引用。内联或在类定义之外定义的c'tor与之无关。

答案 1 :(得分:7)

GCC似乎错误地将自动生成的扣除指南中的T&&视为转发参考:

template <typename T>
B2(T&& x) -> B2<T>;

在这种情况下,T&&是不可转发的r值引用,因为它是一个类参数。相反,GCC错误地推导了T=A&参数类型和B2<T>=B2<A&>类类型,这折叠了构造函数中的引用类型,从而使代码可以使用左值构造函数参数进行编译:

constexpr B2(A& x) noexcept;

类模板自变量推导没有区别内联定义和外联定义。在这种情况下,B2 b21 { a };应该会失败。