You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:01:00