The following piece of code can be compiled using MSVC(v19.38), but failed with GCC(13.2) and Clang(17.0.1). But older version of GCC, e.g. 11.2, works fine.
#include <type_traits>
#ifdef MY_INT
template<typename T, T V> struct my_int
{
static constexpr T value = V;
using value_type = T;
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; }
};
template<auto V> using constant_int_t = my_int<decltype(V), V>;
#else
template<auto V> using constant_int_t = std::integral_constant<decltype(V), V>;
#endif
template<typename T1, typename T2>
using add_t = decltype(T1{} + T2{});
template<typename T1, typename T2>
constexpr auto operator+(T1, T2)
{
return constant_int_t<T1::value + T2::value>{};
}
using v1 = constant_int_t<1>;
using v2 = constant_int_t<2>;
using v3 = add_t<v1, v2>;
static_assert(std::is_same_v<v3, constant_int_t<3>>);
Clang complains about v3 is of type int, which might be ascribed to it fails to spot the operator+ defined after termplate alias add_t and performed an implicit type conversion when dealing with the adding.
If I use my own type of constant int (my_int<T,V>, which is almost the same as std::integral_constant<T,V>), clang will compile it. If I swap the order of definition of add_t and operator+, it also works fine.
So, is this a bug of Clang and GCC? Or an unspecified behaviour?
When you use
my_int, the::(global) namespace is an associated namespace (sincemy_intis defined in the::namespace), soauto ::operator+(T1, T2);can be found via ADL.If you use
std::integral_constant<int, x>, the only associated namespace isstd. Therefore,operator+is looked up in thestd::namespace for ADL, which doesn't find anything that applies (so the builtinint operator+(int, int)is used after converting the arguments)If you moved the
operator+definition beforeadd_t, it can be found via ordinary/non-ADL lookup.