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

C++20下模板重载函数编译失败:模板参数推导问题求助

C++20模板重载函数编译失败解决方案

问题背景

代码在C14/C17下可正常编译,切换到C++20后编译失败,报错找不到匹配的_add_sql_where_op_str重载函数,且无法推导模板参数。

相关代码

重载函数1

template<size_t I, UCHAR Operator, class T, class = std::enable_if_t<Operator == SQL_COMP_OP_IN || Operator == SQL_COMP_OP_NOT_IN>>
auto _add_sql_where_op_str(CIT2String& strSqlStatement, T&& select, typename base_t::template overload_priority<0>)
  -> std::void_t<decltype(std::size(std::forward<T>(select)))
               , decltype(std::begin(std::forward<T>(select)))
               , decltype(std::end(std::forward<T>(select)))
               , decltype(std::begin(std::forward<T>(select)) != std::end(std::forward<T>(select)))
                >
{
  CIT2String ins((ULONG)std::size(std::forward<T>(select)) * 20);
  CIT2String in(20);
  ins += '(';
  bool fFirst = true;
  for (auto&& elem : std::forward<T>(select))
    {
    static_assert(can_make_du<field_t<I>, decltype(elem)>::value, "TableSerialiser: Elements of iterable range are of incorrect type.");

    if (!fFirst)
      ins += ',';
    else
      fFirst = false;

    SelStrMakeFieldData(this->fldinfo_t::dwFieldIDs[I],
                        in.GetPointer(),
                        make_du<field_t<I>>(std::forward<decltype(elem)>(elem)).GetPointer());
    ins += in;
    }
  ins += ')';

  strSqlStatement.GetString() = SelStrAddConditionForField(strSqlStatement.GetString(),
                                                           this->fldinfo_t::dwFieldIDs[I],
                                                           Operator,
                                                           SQL_COMP_OP_TYPE_LITERAL,
                                                           ins.GetPointer());
}

重载函数2

template<size_t I, UCHAR Operator, class T, class = std::enable_if_t<Operator == SQL_COMP_OP_IN || Operator == SQL_COMP_OP_NOT_IN>>
void _add_sql_where_op_str(CIT2String& strSqlStatement, T&& select, typename base_t::template overload_priority<1>)
{
  if constexpr (Operator == SQL_COMP_OP_IN)
    static_assert(false, "TableSerialiser: Select using in operator must be an iterable range.");
  else
    static_assert(false, "TableSerialiser: Select using not-in operator must be an iterable range.");
}

重载函数3

template<size_t I, UCHAR Operator, class T, class = std::enable_if_t<Operator != SQL_COMP_OP_IN && Operator != SQL_COMP_OP_NOT_IN>, void* = 0>
void _add_sql_where_op_str(CIT2String& strSqlStatement, T&& select, typename base_t::template overload_priority<0>)
{
  static_assert(can_make_du<field_t<I>, T&&>::value, "TableSerialiser: Select is of incorrect type.");

  strSqlStatement.GetString() = SelStrAddConditionForField(strSqlStatement.GetString(),
                                                           TableInfos<TableID>::field_infos::dwFieldIDs[I],
                                                           Operator,
                                                           SQL_COMP_OP_TYPE_AUTOMATIC,
                                                           make_du<field_t<I>>(std::forward<T>(select)).GetPointer());
}

调用代码

template<size_t I, UCHAR Operator, class T>
void _add_sql_where_or_str(CIT2String& strSqlStatement, bool& fFirst, T&& select)
{
  if (!fFirst)
    strSqlStatement += " OR ";
  else
    fFirst = false;

  _add_sql_where_op_str<I, Operator>(strSqlStatement, std::forward<T>(select), typename base_t::template priority_tag({}));
}

问题原因

  1. 类型不匹配:调用时传递的priority_tag{}与重载函数参数的overload_priority<N>类型不一致,C++20对模板参数推导的非推导上下文处理更严格,导致无法匹配重载。
  2. SFINAE规则变化:C++20中std::void_t的SFINAE行为调整,原返回值约束的写法可能无法正确触发重载决议。
  3. 模板参数推导歧义:部分模板参数的约束方式在C++20下导致推导失败。

修复方案

1. 统一优先级标签类型

修改调用代码,传递与重载函数参数匹配的overload_priority<0>类型:

template<size_t I, UCHAR Operator, class T>
void _add_sql_where_or_str(CIT2String& strSqlStatement, bool& fFirst, T&& select)
{
  if (!fFirst)
    strSqlStatement += " OR ";
  else
    fFirst = false;

  // 使用与重载函数匹配的overload_priority<0>
  _add_sql_where_op_str<I, Operator>(strSqlStatement, std::forward<T>(select), typename base_t::template overload_priority<0>{});
}

2. 改用C++20 requires表达式优化约束

将原std::enable_if_t和std::void_t的约束替换为更清晰的requires表达式,避免SFINAE歧义:

修改重载函数1

template<size_t I, UCHAR Operator, class T>
// 先约束Operator类型,再约束T的可迭代性
requires (Operator == SQL_COMP_OP_IN || Operator == SQL_COMP_OP_NOT_IN) &&
         requires(T&& t) {
           std::size(std::forward<T>(t));
           std::begin(std::forward<T>(t));
           std::end(std::forward<T>(t));
           std::begin(std::forward<T>(t)) != std::end(std::forward<T>(t));
         }
void _add_sql_where_op_str(CIT2String& strSqlStatement, T&& select, typename base_t::template overload_priority<0>)
{
  // 函数体保持不变
}

修改重载函数3

template<size_t I, UCHAR Operator, class T>
requires (Operator != SQL_COMP_OP_IN && Operator != SQL_COMP_OP_NOT_IN)
void _add_sql_where_op_str(CIT2String& strSqlStatement, T&& select, typename base_t::template overload_priority<0>)
{
  static_assert(can_make_du<field_t<I>, T&&>::value, "TableSerialiser: Select is of incorrect type.");

  strSqlStatement.GetString() = SelStrAddConditionForField(strSqlStatement.GetString(),
                                                           TableInfos<TableID>::field_infos::dwFieldIDs[I],
                                                           Operator,
                                                           SQL_COMP_OP_TYPE_AUTOMATIC,
                                                           make_du<field_t<I>>(std::forward<T>(select)).GetPointer());
}

3. 移除冗余模板参数

重载函数3中的void* = 0是C14时期用于避免重载歧义的技巧,C20下改用requires后可直接移除,简化模板定义。

验证效果

修改后代码可在C20标准下正常编译,同时保持对C17的兼容性(若保留enable_if和void_t的写法,需确保编译器支持C++17及以上)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:54:59