https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
--- Comment #5 from Jonathan Wakely <redi at gcc dot gnu.org> --- (In reply to Pavel Novikov from comment #4) > Indeed the standard says all over that narrowing conversion in > initialization is prohibited, though this code compiles: > > int i = 42; > bool b[] = {i}; // narrowing > int64_t i64[] = {i}; > double d[] = {i}; // narrowing* They compile *with a diagnostic* (a warning, specifically). That is what the standard requires for ill-formed code, and those narrowing conversions are ill-formed. If you want them to be errors not warnings, you can use -Werror=narrowing. In a SFINAE context, they result in substitution failure, because "warn and continue" is not an option for substitution failures. > (*putting aside that any `int` can be _exactly_ represented by a double > precision floating point value) Assuming 32-bit int and 64-bit double, yes. That isn't guaranteed by the standard though: int could have 64 bits, for example. To avoid unportable code, it's considered to be narrowing "except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type". So double d{42} is not a narrowing conversion, but double d{i} is. > As an approximation for `T x[] = {t}` requirement I came up with this code: > > template<typename T, typename U> > std::enable_if_t<sizeof(decltype(T{std::declval<U>()}))> > test() {} > > and indeed it fails to compile when narrwoing conversion is required, i.e. > > test<bool, int>(); // error: narrowing > test<int64_t, int>(); > test<double, int>(); // error: narrowing > > So turning > > > an imaginary function FUN(Ti) for each alternative type Ti for which Ti x[] > > = {std::forward<T>(t)}; is well-formed for some invented variable x. > > into code using the approximation above we have the following code > > template<typename T, typename U> > std::enable_if_t<sizeof(decltype(T{std::declval<U>()}))> > F(U) {} > > F<bool>(42); // error: deduction/substitution failed due to narrowing > F<int64_t>(42); > F<double>(42); // error: deduction/substitution failed due to narrowing > > And if we consider `F<T_j>()` functions as an overload set, then indeed only > `F<int64_t>()` is well formed considering (non)narrowing requirement and is > ultimately selected. > (See https://godbolt.org/z/sxof45Eeh) > > Did I get it right? Yes. Libstdc++ uses something similar to your enable_if, but using void_t instead: template<typename _Ti> struct _Arr { _Ti _M_x[1]; }; template<size_t _Ind, typename _Tp, typename _Ti> struct _Build_FUN<_Ind, _Tp, _Ti, void_t<decltype(_Arr<_Ti>{{std::declval<_Tp>()}})>> The _Arr struct is needed to ensure that we're trying to convert to T{1} not just T, which is what the standard wants.