C++如何从类构造函数推导类成员的模板参数
错误原因
你的判断基本准确,现有代码的编译错误来自两个核心问题:
- 你为
A声明的是模板模板参数,template <template <bool LengthOpt> class T>中的T本身是模板而非具体类型,C++语法不允许直接将模板作为函数形参类型、非静态成员变量类型使用——必须为T传入具体的bool非类型实参得到实例化类型(如B<false>、B<true>),才能作为类型使用,这是前两条编译错误的直接来源。 - C++的类模板参数推导(CTAD)不会自动做跨类型的隐式构造推导:你写
A a("hello")时,编译器只会尝试用传入的const char*类型直接匹配A的模板形参,不会自动先将const char*构造成B实例再反向推导A的模板参数,这是第三条报错的来源。
可行实现
这个需求完全可以通过C++17及以上版本的现有语法实现,不需要特殊扩展,修正后的代码如下:
#include <iostream> #include <cstddef> #include <utility> template <bool LengthOpt> struct B { B(const char*) { } B(const char*, size_t) { } void print() { if constexpr (LengthOpt) { std::cout << "LengthOpt is set" << std::endl; } else { std::cout << "LengthOpt is not set" << std::endl; } } }; // 保留B的原有推导指引 B(const char*) -> B<false>; B(const char*, size_t) -> B<true>; // 将A的模板参数改为接收具体实例化类型,放弃模板模板参数 template <class BInstance> struct A { // 构造函数用可变参完美转发,支持传入构造B实例所需的任意参数 template <class... Args> A(Args&&... args) : is_{std::forward<Args>(args)...} { } void print() { is_.print(); } BInstance is_; }; // 为A添加自定义推导指引,匹配const char*和const char*+size_t两种入参 A(const char*) -> A<B<false>>; A(const char*, size_t) -> A<B<true>>; int main() { A a1("hello"); a1.print(); // 输出:LengthOpt is not set A a2("hello", 5); a2.print(); // 输出:LengthOpt is set }
实现说明
- 将
A的模板参数从模板模板参数改为普通类型参数,直接接收实例化后的B类型,解决了模板不能直接作为类型使用的语法错误。 - 构造函数采用可变参完美转发,既支持直接传入已经构造完成的B实例,也支持传入构造B所需的参数直接在A内部初始化成员,避免不必要的拷贝。
- 为A添加自定义CTAD推导指引后,编译器可以根据传入的参数自动匹配正确的B实例化类型,不需要手动书写A的模板参数。
- 如果需要后续修改B的推导规则时不用同步修改A的推导指引,可以将A的推导指引写成泛型版本,自动复用B的推导逻辑:
template <class... Args> A(Args&&... args) -> A<decltype(B(std::declval<Args>()...))>; - 该实现依赖C17引入的类模板参数推导、用户自定义推导指引特性,C17之前的标准不支持这种隐式推导,必须手动指定模板参数。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

