MSVC constexpr函数'xyz'不能导致常量表达式

Tim*_*imo 10 c++ visual-c++ c++17

我已经创建了一个函数,它将多个较小的值连接成一个较大的值,同时保留值的bianry表示(例如,int argb从多个构建一个unsigned char r, g, b, a).我知道我也可以通过改变价值来实现这一点,但这不是这个问题的问题.

但是,如果我使用该函数实际从这些值生成一个整数,msvc会抛出编译器错误:

error C3615: constexpr function 'Color::operator int' cannot result in a constant expression
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of '<lambda_dcb9c20fcc2050e56c066522a838749d>::operator ()'
Run Code Online (Sandbox Code Playgroud)

是一个完整的样本.Clang和gcc编译代码但msvc拒绝:

#include <type_traits>
#include <memory>

namespace detail
{
    template <typename From, typename To, size_t Size>
    union binary_fusion_helper
    {
        const From from[Size];
        const To to;
    };

    template <typename To, typename Arg, typename ...Args, typename = std::enable_if_t<(... && std::is_same_v<std::remove_reference_t<Arg>, std::remove_reference_t<Args>>)>>
    constexpr To binary_fusion(Arg arg, Args... args)
    {
        using in_t = std::remove_reference_t<Arg>;
        using out_t = To;
        static_assert(sizeof(out_t) == sizeof(in_t) * (sizeof...(Args) + 1), "The target type must be of exact same size as the sum of all argument types.");
        constexpr size_t num = sizeof(out_t) / sizeof(in_t);
        return binary_fusion_helper<in_t, out_t, num> { std::forward<Arg>(arg), std::forward<Args>(args)... }.to;
    }
}

template <typename To>
constexpr auto binary_fusion = [](auto ...values) -> To
{
    return detail::binary_fusion<std::remove_reference_t<To>>(values...);
};

struct Color
{
    float r, g, b, a;

    explicit constexpr operator int() const noexcept
    {
        return binary_fusion<int>(static_cast<unsigned char>(r * 255), static_cast<unsigned char>(g * 255),
                                  static_cast<unsigned char>(b * 255), static_cast<unsigned char>(a * 255));
    }
};
Run Code Online (Sandbox Code Playgroud)

clang和gcc只是忽略代码永远不会作为constexpr运行或msvc错误?如果msvc是正确的,为什么函数不能在编译时运行?

Bar*_*rry 15

每个编译器都是正确的 [dcl.constexpr]/5中的规则是:

对于既不是默认也不是模板的constexpr函数或constexpr构造函数,如果不存在参数值,则函数或构造函数的调用可以是核心常量表达式的计算子表达式,或者对于构造函数,可以是常量初始化函数.一些对象([basic.start.static]),程序格式错误,无需诊断.

没有一组参数可以传入,binary_fusion这样就可以将它作为核心常量表达式进行求值,因此声明它constexpr是不正确的NDR.这种情况的原因是因为detail::binary_fusion()初始化一个活动成员的联合,然后从非活动成员读取,在常量表达式中不允许这样做([expr.const] /4.8):

应用于glvalue的左值到右值转换,该glvalue引用联合或其子对象的非活动成员;

MSVC以某种方式诊断出这一点,gcc/clang不会发生.所有编译器都正确诊断了这个:

constexpr Color c{1.0f, 1.0f, 1.0f, 1.0f};
constexpr int i = static_cast<int>(c); // error: not a constant expression
Run Code Online (Sandbox Code Playgroud)

  • @RobertAndrzejuk它大致翻译为"实际阅读它". (2认同)