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

C++模板Args&&...万能引用引发std::conditional C2975编译错误

C2975编译错误:std::conditional要求编译期常量表达式问题排查

环境信息

  • 操作系统:Windows
  • 编译器环境:Visual Studio 2017
  • 业务自定义逻辑:定义TSTRING宏,WIN32环境下映射为std::string,x64环境下映射为std::wstring
  • 开发目标:编写模板实现类字符串类型到std::wstring的自动转换,支撑不同字符串类型自动转为TSTRING类型

错误信息

编码过程中触发C2975编译错误:

Error   C2975   
'_Test': invalid template argument for 'std::conditional', expected compile-time constant expression

复现代码

#include <codecvt>
#include <iostream>

std::wstring to_wide_string(const std::string& input)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t> > converter;
    return converter.from_bytes(input);
}

std::string to_byte_string(const std::wstring& input)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t> > converter;
    return converter.to_bytes(input);
}


// 检测Args是否可用于F的构造函数,例:check<std::string>(3, 'a').value == true
template <class F>
struct check
{
    template <class... Args>
    constexpr check(Args&&... args) : value(
        std::is_void<
            decltype(t2<F, Args...>(0, std::forward<Args>(args)...))
        >::type::value) {}

    bool value;

    struct nat {};
    template <class G, class... GArgs>
    static auto t2(int, GArgs&&... args) -> decltype(G(std::forward<GArgs>(args)...), void());
    template <class, class...>
    static auto t2(...)->nat;
};

// 非宽字符串场景转换为std::string(未完成)
template <typename T, bool is_wstring = std::is_same<std::wstring, std::decay_t<T>>::value>
struct to_tstring : std::string
{
};

// 宽字符串场景转换为std::wstring
template <typename T>
struct to_tstring<T, true> : std::wstring
{
    template <typename... Args>
    // 将Args&&...修改为Args...即可编译通过
    to_tstring(Args&&... args) : to_tstring<T, true>(
        std::conditional<
            check<std::string>(std::forward<Args>(args)...).value,
            std::true_type, std::false_type
        >::type(),
        std::forward<Args>(args)...
        ) 
    {
        std::cout << "to_tstring<T, true>::ctor_1" << std::endl;
    }

private:
    template <typename... Args>
    to_tstring(std::false_type, Args&&... args) : std::wstring(std::forward<Args>(args)...) {
        std::cout << "to_tstring<T, true>::ctor_2 false_type" << std::endl;
    }

    template <typename... Args>
    to_tstring(std::true_type, Args&&... args) : std::wstring(to_wide_string(std::string(std::forward<Args>(args)...))) {
        std::cout << "to_tstring<T, true>::ctor_3 true_type" << std::endl;
    }
};


int main()
{
    to_tstring<std::wstring> a("asdf"); // 触发编译错误
    return 0;
}

异常现象

  1. 单独在main函数中使用check类获取value时,可正常作为constexpr编译期常量使用,测试代码如下:
    int main()
    {
       constexpr int i = check<std::string>(3,'j').value;
       int s[i] = {};   // 编译正常
    }
    
  2. 将to_tstring构造函数签名从to_tstring(Args&&... args)修改为to_tstring(Args... args)后,编译可正常通过,原理未知。

根本原因

  • std::conditional的模板参数要求必须是编译期常量表达式,错误触发的核心是在转发引用的构造函数初始化列表语境下,VS2017无法将check<std::string>(std::forward<Args>(args)...).value识别为编译期常量:
    构造函数的形参args属于函数作用域的运行期实体,哪怕传入的实参是编译期已知的字面量,在转发引用(Args&&万能引用)的推导逻辑下,编译器会将形参视为运行期变量,导致check的constexpr构造函数无法在编译期完成求值,返回的value自然不满足模板参数的常量要求。
  • 单独在main函数中调用check<std::string>(3,'j').value可以正常编译,是因为该语境下直接将编译期常量作为实参传入,没有经过函数形参中转,编译器可以直接在编译期完成constexpr构造的全流程求值,得到的value是合法的编译期常量。
  • 将构造函数参数改为Args... args(值传递)后可编译通过,是因为值传递场景下,VS2017对传入的编译期常量实参做常量求值时,不会将形参判定为运行期实体,check的constexpr构造可以正常在编译期完成计算,value满足std::conditional的参数要求。

注:该行为属于VS2017对C++17 constexpr规则的旧实现特性,更高版本的MSVC以及GCC/Clang对该场景的常量求值支持更完善,不会触发同类错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 19:03:42