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

C++从concept推导模板参数时默认Policy参数推导失败

问题成因
  • CTAD(类模板参数推导)的推导指引存在逻辑错误:推导指引的所有模板参数必须能从构造函数的传入实参中完成推导,原有代码的推导指引中引入了Policy模板参数,但该参数完全没有出现在构造函数的参数列表中,编译器没有任何信息可以推导该类型,因此直接忽略这个推导候选。
  • 模板参数顺序设计不合理:需要支持显式指定的Policy被放在模板参数列表末尾,排在它前面的T是依赖谓词自动推导的类型,这种情况下写Foo<Bar1>(fun)时,编译器会将Bar1匹配到第一个模板参数T,完全无法识别为用户想要指定的Policy类型,不符合使用预期。
  • make_foo实现存在错误:原有两个重载都存在问题:第一个重载的Policy模板参数没有设置默认值,调用时不手动指定就无法完成推导,且返回语句中Foo<P1, Policy>的模板参数顺序和类定义完全不匹配,直接触发类型错误;第二个重载同样存在Policy无默认值、调用时无法推导的问题。
修改方案
  1. 调整类模板参数顺序:将Policy放在参数列表第一位,设置默认值为Bar1;谓词类型P1放在第二位,支持从构造函数实参自动推导;谓词参数类型T放在第三位,默认通过萃取从P1得到,不需要用户手动指定,同时通过concept约束校验P1确实是符合要求的谓词。
  2. 修正CTAD推导指引:推导指引仅对应「完全不指定模板参数、直接传入谓词构造」的场景,此时直接使用默认的Bar1作为Policy类型,不需要在推导指引中引入无法推导的额外参数。
  3. 重写make_foo实现:统一为单个函数,同样将Policy放在模板参数第一位并设置默认值,内部通过CTAD构造对象,不需要编写多个重载。
修正后可运行代码
#include <concepts>
#include <utility>
#include <string>
#include <functional>

struct Bar1{};
struct Bar2{};

// 萃取单参数可调用对象的参数类型
template<typename T> struct single_function_argument;
template<typename Ret, typename Arg> 
struct single_function_argument<std::function<Ret(Arg)>> { using type = Arg; };

// 类模板参数顺序说明:
// 1. Policy:策略类型,首位带默认值Bar1,支持用户显式指定
// 2. P1:谓词类型,从构造函数传入的实参自动推导
// 3. T:谓词的参数类型,默认从P1萃取,无需手动指定
template<
    class Policy = Bar1,
    class P1,
    class T = typename single_function_argument<decltype(std::function{std::declval<P1>()})>::type
>
requires std::predicate<P1, T>
struct Foo : Policy
{
    P1 _f;
    Foo(P1 &&f): _f(std::forward<P1>(f)) {}
};

// CTAD推导指引:无显式指定模板参数时,默认使用Bar1作为Policy
template<class P1>
Foo(P1 &&) -> Foo<
    Bar1,
    P1,
    typename single_function_argument<decltype(std::function{std::declval<P1>()})>::type
>;

// 统一的make_foo实现
template<class Policy = Bar1, class P1>
auto make_foo(P1 &&f)
{
    return Foo<Policy>(std::forward<P1>(f));
}

int main() {
    auto fun = [](const std::string &s){return s == "toto";};
    // 默认Policy构造
    auto my_foo1 = Foo(fun);
    auto my_foo2 = make_foo(fun);
    // 显式指定Bar1作为Policy
    auto my_foo3 = Foo<Bar1>(fun);
    auto my_foo4 = make_foo<Bar1>(fun);
    // 显式指定Bar2作为Policy
    auto my_foo5 = Foo<Bar2>(fun);
    auto my_foo6 = make_foo<Bar2>(fun);
}

上述代码可在Clang14及以上、GCC12及以上版本正常编译,完全支持所有预期场景:不指定Policy时自动使用默认的Bar1,显式指定Policy时仅需传入第一个模板参数,剩余类型全部自动从传入的谓词推导。

内容的提问来源于stack exchange,提问作者WaterFox

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 22:45:44