内部类中的通用复制构造函数
generalized copy constructor in an inner class
我想指定从A<T>::B
到A<U>::B
的转换。
template<typename T>
struct A
{
struct B
{
B() {}
template<typename U>
B(const typename A<U>::B& rhs) {}
};
};
int main()
{
A<int>::B x;
A<double>::B y = x;
}
我认为这将做到这一点,但我得到编译器错误:
请求从' A
::B '转换为非标量类型' A ::B ' "
为什么我的代码不正确,实现所需转换的正确方法是什么?
模板不能是复制构造函数。§12.8/2,脚注:
因为模板构造函数永远不是复制构造函数,所以存在这样一个模板并不会抑制复制构造函数的隐式声明。模板构造函数与其他构造函数(包括复制构造函数)一起参与重载解析,如果模板构造函数比其他构造函数提供更好的匹配,则可以使用模板构造函数来复制对象。
由于模板的实参是const &
,它的签名将与复制情况下隐式声明的函数完全相同,因此它永远不会用作复制构造函数。
这可能没有问题,因为在示例中您将其用作转换构造函数。然而,::
之前的模板参数是一个非推导的上下文,因此编译器不能插入A<int>::B
并解析int
。由于专门化模板的方法多种多样,编译器无法确定哪个A
(如果有的话)符合条件。您可以在A<float>
中添加typedef A<int>::B B;
,然后int
和float
都将符合U
。
您可以通过使用SFINAE并向类添加成员类型来解决这个问题,以帮助导航层次结构。这是一个演示。
#include <typeinfo>
#include <iostream>
template<typename T>
struct A
{
typedef T type;
struct B
{
B() {}
template<typename U>
B(const U& rhs, typename U::nest_A_parent * = NULL ) {
std::cout << "copied from type "
<< typeid( typename U::nest_A_parent::type ).name() << 'n';
}
private:
typedef A nest_A_parent;
template< typename U >
friend struct B;
};
};
int main()
{
A<int>::B x;
A<double>::B y( x );
}
稍微修改了源代码,以说明这是关于类型推导系统的限制:
template<typename T>
struct A
{
struct B
{
B() {}
template<typename U>
B(const typename A<U>::B& rhs) {}
template<typename U>
B& operator=(const typename A<U>::B& rhs) {}
template<typename U>
B& something(const typename A<U>::B& rhs) {}
};
};
int main()
{
A<int>::B x;
A<double>::B y(x); // fails to deduce
A<double>::B y = x; // fails to deduce
A<double>::B y; y = x; // fails to deduce
x.something(y); // fails to deduce
x.something<double>(y);// NO PROBLEM
}
您可以看到,当我们稍微帮助编译器时,就没有更多的问题了。还要注意实际的编译器错误(gcc)显示了它的混乱:
test.cpp|24 col 15| note: candidate is:
test.cpp|15 col 44| note: template<class U> A<T>::B& A<T>::B::something(const typename A<U>::B&)
[with U = U, T = int, A<T>::B = A<int>::B, typename A<U>::B = A<T>::B]
注意U = U
(即未解析)
如何做一个转换构造函数(正如Potatoswatter指出的,根据定义它不能是复制构造函数),它只匹配嵌套类型B
,对于任何A<T>::B
:
namespace detail {
// private type to identify all A<T>::B
struct B {};
} // detail
// trait to identify all A<T>::B
// a template alias could also be used here
template<typename T>
struct is_b: std::is_base_of<detail::B, T> {};
template<typename T>
struct A
{
struct B: detail::B {
B() {}
template<typename U>
B(U&& u)
{
static_assert( is_b<typename std::decay<U>::type>::value
, "Conversion only allowed from A<T>::B" );
}
};
};
该技术的优点是它不使用SFINAE,因此无效的转换尝试将通过static_assert
报告,而不是无声地失败。另一方面,如果至少有一个其他模板转换构造函数,则需要SFINAE。
这是假设A<T>::B
不同于A<U>::B
(对于不同的T
和U
)。如果嵌套类型是相同的(就像您简化的示例代码中的情况一样),您最好在其他地方定义B
,并在A
中使用typedef some_private_namespace::B B;
。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 复制构造函数、赋值运算符C++
- std::ofstream 作为类成员删除复制构造函数?
- 复制构造函数C++无法正确复制指针
- 关于复制构造函数的一个棘手问题
- 为什么调用复制构造函数而不是移动构造函数?
- 填充上编译器生成的复制构造函数之间的不一致
- C++ 对象指针数组的复制构造函数
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 防止在复制构造函数中隐式调用基构造函数
- 为用户定义的类正确调用复制构造函数/赋值运算符
- 具有已删除移动和复制构造函数的类的就地构造
- 复制构造函数隐式转换问题
- 复制构造函数中的递归调用