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

ple*_*ndo 13 c++ gcc language-lawyer template-argument-deduction c++17

请考虑以下代码

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;
 }
Run Code Online (Sandbox Code Playgroud)

产量

mctor from 2
mctor from 3
Run Code Online (Sandbox Code Playgroud)

所以它看起来好像外部定义的构造函数完全转发其参数的值类别,而内联定义的构造函数则不然.

是否将外部定义的构造函数处理为函数模板(完全转发其参数)或此处发生了什么?

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

我正在使用GCC 7.2.0.

Sto*_*ica 9

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

[temp.deduct.call](强调我的)

3转发参考是一个rvalue参照CV-不合格模板参数不代表一类模板的模板参数(类模板参数推导过程中([over.match.class.deduct])).如果P是转发引用且参数是左值,则使用类型"对A的左值引用"代替A来进行类型推导.

在这两种情况下T,在CTAD期间命名封闭类的模板参数,因此它不应该以任何方式生成转发引用.内联或在类定义之外定义的c'tor与此无关.


Pio*_*cki 7

看起来GCC错误地将T&&自动生成的演绎指南视为转发参考:

template <typename T>
B2(T&& x) -> B2<T>;
Run Code Online (Sandbox Code Playgroud)

在这种情况下T&&是非转发r值引用,因为它是一个类参数.相反,GCC错误地推断出T=A&参数类型和B2<T>=B2<A&>类类型,这会折叠构造函数中的引用类型,允许代码使用左值构造函数参数进行编译:

constexpr B2(A& x) noexcept;
Run Code Online (Sandbox Code Playgroud)

类模板参数推导不区分内联和外联定义.在这种特殊情况下,B2 b21 { a };应该失败.