考虑以下代码
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。
答案 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 };
应该会失败。