値の2進数表現を維持しながら、複数の小さな値を1つの大きな値に連結する関数を作成しました(例: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 ()'
ここ は完全なサンプルです。 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));
}
};
Clangとgccは、コードがconstexprとして実行されないこと、またはmsvcが間違っていることを単に無視しますか?そして、msvcが正しい場合、コンパイル時に関数を実行できないのはなぜですか?
すべてのコンパイラは正しいです。 [dcl.constexpr]/5 のルールは次のとおりです。
デフォルトでもテンプレートでもないconstexpr関数またはconstexprコンストラクターの場合、引数値が存在しないため、関数またはコンストラクターの呼び出しは、コア定数式の評価された部分式、またはコンストラクターの場合は定数初期化子になります。一部のオブジェクト([basic.start.static])、プログラムの形式が正しくないため、診断は必要ありません。
コア定数式として評価できるようにする_binary_fusion
_に渡すことができる引数のセットはないため、constexpr
の宣言は不正な形式のNDRです。これが事実である理由は、detail::binary_fusion()
が1つのアクティブなメンバーを使用してユニオンを初期化し、定数式( [expr.const] /4.8 ):
ユニオンまたはそのサブオブジェクトの非アクティブメンバーを参照する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
_