转换具有不同模板参数的模板对象,这怎么可能?
Cast a template object with different template parameters, how is this possible?
今天我尝试了一些东西,认为它行不通,但它确实有效,我不明白为什么...
我有一个 base
类型的对象,它不包含任何内容。
另一个继承自base
的derived
类型,存储一个函数指针并调用它。
derived
是一个模板对象,它的参数是存储的函数等待的参数类型。
如果我从类型 derived<void>
的对象引用类型 base
并尝试将其转换为 derived<int>
... 它确实有效。
derived<>
和derived< int >
是两种不同的类型,你怎么解释?
一个简单的例子:
#include <iostream>
void foo( int ) { ::std::cout << "void foo( int )\n"; }
void foo() { ::std::cout << "void foo()\n"; }
struct base
{
base() { ::std::cout << "\tcreate base\n"; }
base( const base & ) { ::std::cout << "\tcopy base\n"; }
base( base && ) { ::std::cout << "\tmove base\n"; }
void operator =( const base & ) { ::std::cout << "\tcopy assignment base\n"; }
void operator =( base && ) { ::std::cout << "\tmove assignment base\n"; }
};
template < typename ... Args >
struct derived : public base
{
derived() { ::std::cout << "\tcreate derived\n"; }
derived( const derived & ) { ::std::cout << "\tcopy derived\n"; }
derived( derived && ) { ::std::cout << "\tmove derived\n"; }
derived & operator =( const derived & ) { ::std::cout << "\tcopy assignment derived\n"; }
derived & operator =( derived && ) { ::std::cout << "\tmove assignment derived\n"; }
void operator ()( Args ... args )
{
f( args ... );
}
void ( *f )( Args ... );
};
int main()
{
derived<> derived_void;
derived_void.f = ( void ( * )() )&foo;
base & base_void = derived_void;
derived<> &test = derived_void;
static_cast< derived< int > & >( base_void )( 1 ); // compiles, makes no copy
// static_cast< derived< int > & >( test )( 1 ); // doesn't compile
}
感谢您的帮助。
考虑一下:
struct X {};
struct A : X {};
struct B : X {};
X & x = get_object_ref(); //can return A& or B&, how would I know?
A & a = static_cast<A&>(x); //this will compile!
B & b = static_cast<B&>(x); //this will compile too!
如果 static_cast<A&>(x)
应该给出错误,那么 static_cast<B&>(x)
应该给出 错误 以及对称性。既然一个都要编译,那么两个都要编译。
底线:base&
总是可以向下转换为 derive&
— 这是一种语言特性。您有责任投射到 正确 derive&
.
忘掉模板吧。
您有一个 class A
、一个派生自 A
的 class B
和一个 class C
派生自 A
.
如果您有一个 A
类型的左值,您可以将其转换为 B &
或 C &
。编译器通常无法知道具体派生类型是什么,因此它相信您知道自己在做什么。
这正是您正在做的:您正在将 A
转换为 B &
。您的 A
实际上是一个 C
对象,但编译器不知道。
今天我尝试了一些东西,认为它行不通,但它确实有效,我不明白为什么...
我有一个 base
类型的对象,它不包含任何内容。
另一个继承自base
的derived
类型,存储一个函数指针并调用它。
derived
是一个模板对象,它的参数是存储的函数等待的参数类型。
如果我从类型 derived<void>
的对象引用类型 base
并尝试将其转换为 derived<int>
... 它确实有效。
derived<>
和derived< int >
是两种不同的类型,你怎么解释?
一个简单的例子:
#include <iostream>
void foo( int ) { ::std::cout << "void foo( int )\n"; }
void foo() { ::std::cout << "void foo()\n"; }
struct base
{
base() { ::std::cout << "\tcreate base\n"; }
base( const base & ) { ::std::cout << "\tcopy base\n"; }
base( base && ) { ::std::cout << "\tmove base\n"; }
void operator =( const base & ) { ::std::cout << "\tcopy assignment base\n"; }
void operator =( base && ) { ::std::cout << "\tmove assignment base\n"; }
};
template < typename ... Args >
struct derived : public base
{
derived() { ::std::cout << "\tcreate derived\n"; }
derived( const derived & ) { ::std::cout << "\tcopy derived\n"; }
derived( derived && ) { ::std::cout << "\tmove derived\n"; }
derived & operator =( const derived & ) { ::std::cout << "\tcopy assignment derived\n"; }
derived & operator =( derived && ) { ::std::cout << "\tmove assignment derived\n"; }
void operator ()( Args ... args )
{
f( args ... );
}
void ( *f )( Args ... );
};
int main()
{
derived<> derived_void;
derived_void.f = ( void ( * )() )&foo;
base & base_void = derived_void;
derived<> &test = derived_void;
static_cast< derived< int > & >( base_void )( 1 ); // compiles, makes no copy
// static_cast< derived< int > & >( test )( 1 ); // doesn't compile
}
感谢您的帮助。
考虑一下:
struct X {};
struct A : X {};
struct B : X {};
X & x = get_object_ref(); //can return A& or B&, how would I know?
A & a = static_cast<A&>(x); //this will compile!
B & b = static_cast<B&>(x); //this will compile too!
如果 static_cast<A&>(x)
应该给出错误,那么 static_cast<B&>(x)
应该给出 错误 以及对称性。既然一个都要编译,那么两个都要编译。
底线:base&
总是可以向下转换为 derive&
— 这是一种语言特性。您有责任投射到 正确 derive&
.
忘掉模板吧。
您有一个 class A
、一个派生自 A
的 class B
和一个 class C
派生自 A
.
如果您有一个 A
类型的左值,您可以将其转换为 B &
或 C &
。编译器通常无法知道具体派生类型是什么,因此它相信您知道自己在做什么。
这正是您正在做的:您正在将 A
转换为 B &
。您的 A
实际上是一个 C
对象,但编译器不知道。