C++20/23类模板实参推导:显式与推导实参混合使用问题
问题描述
我编写了一个需要两个模板类型的类,其中一个希望调用者显式指定,另一个期望由编译器自动推导,但当前编译器要求必须显式指定后者。注释掉foo2的定义后编译正常,请问有没有办法让foo2这种只显式指定一个模板参数的变量构造生效?
(示例程序用于统计为显式指定类型分配内存的次数,计数器可为任意数值类型)
#include <cstdlib> template<class T, class U> class Foo { public: Foo( U* counter_in ) : counter( counter_in ) {}; U* counter; T* Alloc() { *counter += 1; return (T*) malloc( sizeof(T) ); }; }; int iCounter = 0; Foo<double, int> foo1( &iCounter ); Foo<double> foo2( &iCounter ); int main( int nArg, const char* apszArg[] ) { double* pd = foo1.Alloc(); return 0; }
编译器报错信息:
> g++ -std=c++23 -c -o t.o t.cpp t.cpp:12:11: error: wrong number of template arguments (1, should be 2) 12 | Foo<double> foo2( &iCounter ); | ^ t.cpp:3:34: note: provided for 'template<class T, class U> class Foo' 3 | template<class T, class U> class Foo { | ^~~ t.cpp:12:19: error: invalid conversion from 'int*' to 'int' [-fpermissive] 12 | Foo<double> foo2( &iCounter ); | ^~~~~~~~~ | | | int* t.cpp: In function 'int main(int, const char**)': t.cpp:15:18: error: 'foo' was not declared in this scope; did you mean 'Foo'? 15 | double* pd = foo.Alloc(); | ^~~ | Foo > g++ -v Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/14/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-redhat-linux Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,objc,obj-c++,ada,go,d,m2,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-libstdcxx-backtrace --with-libstdcxx-zoneinfo=/usr/share/zoneinfo --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl=/builddir/build/BUILD/gcc-14.0.1-20240411/obj-x86_64-redhat-linux/isl-install --enable-offload-targets=nvptx-none,amdgcn-amdhsa --enable-offload-defaulted --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1 Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 14.0.1 20240411 (Red Hat 14.0.1-0) (GCC)
解决方案
方法1:工厂函数模板(兼容C++11及以上)
编写一个辅助模板函数,通过显式指定T类型,让编译器自动推导U类型,完美匹配需求:
#include <cstdlib> template<class T, class U> class Foo { public: Foo(U* counter_in) : counter(counter_in) {}; U* counter; T* Alloc() { *counter += 1; return (T*)malloc(sizeof(T)); }; }; // 工厂函数:显式指定T,推导U template<class T, class U> Foo<T, U> make_Foo(U* counter) { return Foo<T, U>(counter); } int iCounter = 0; Foo<double, int> foo1(&iCounter); auto foo2 = make_Foo<double>(&iCounter); // 显式指定T为double,U由参数推导为int int main(int nArg, const char* apszArg[]) { double* pd = foo1.Alloc(); double* pd2 = foo2.Alloc(); return 0; }
方法2:C20部分显式模板参数(需C20及以上标准)
C++20允许在类模板实例化时用auto占位未指定的模板参数,编译器会根据构造函数参数自动推导该类型:
#include <cstdlib> template<class T, class U> class Foo { public: Foo(U* counter_in) : counter(counter_in) {}; U* counter; T* Alloc() { *counter += 1; return (T*)malloc(sizeof(T)); }; }; int iCounter = 0; Foo<double, int> foo1(&iCounter); Foo<double, auto> foo2(&iCounter); // U由编译器自动推导为int int main(int nArg, const char* apszArg[]) { double* pd = foo1.Alloc(); double* pd2 = foo2.Alloc(); return 0; }
编译时需指定C++20及以上标准:g++ -std=c++20 t.cpp
注意事项
类模板推导指引(C17引入)无法直接解决这个问题,因为Foo的构造函数没有任何T类型的参数,编译器无法从构造函数参数推导T,必须显式指定T。因此工厂函数或C20的部分显式参数是更直接的解决方案。
内容的提问来源于stack exchange,提问作者Swiss Frank
相关产品推荐
相关产品推荐

