下面这段代码可以使用GCC或MSVC编译,但使用Clang编译失败
#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 抱怨
v3
是 int 类型,这可能是因为它未能发现在 termplate 别名 operator+
之后定义的 add_t
,并在处理添加时执行了隐式类型转换。
如果我使用自己的常量 int 类型(
my_int<T,V>
,与std::integral_constant<T,V>
几乎相同),clang 会编译它。如果我交换 add_t
和 operator+
的定义顺序,它也可以正常工作。
那么,这是 clang 的 bug 吗?还是未指定的行为?
我认为这是 Clang 的一个错误。
当 template-id 引用别名模板的特化时,它相当于用其 template-argument 替换别名模板的定义类型 id 中的模板参数而获得的关联类型。
所以
using v3 = add_t<v1, v2>;
应等于
using v3 = decltype(v1{} + v2{});
但 Clang 将它们视为不同类型。