GCC 中模板化转换运算符中的错误:解决方法?
Bug in templated conversion operator in GCC: Workaround?
我想要一个 class 代表具有某种维度的单位。这应该表达类似 1.5m^2 的内容。应允许与某种类型进行标量乘法,无量纲单位的行为应与基础类型完全相同。这是我的解决方案:
#include <type_traits>
template<typename T, int Dim>
class Unit {
public:
explicit Unit(T t): _value(t) {}
template<int D = Dim, typename std::enable_if_t<D==0, int> = 0>
operator T() { static_assert(Dim==0, ""); return _value; } //static_assert not necessary, but gives error if template is removed
T _value;
};
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
这在 clang (https://godbolt.org/z/8Pev7W6Y1). However, due to a GCC bug with templated conversion operators () 中工作得很好,在 GCC 中不起作用。
无法删除 SFINAE 结构,因为它(正确地)遇到了 static_assert
。
您是否有同样适用于 GCC 的等效代码的想法?代码应该在 C++17 中与两个编译器一起工作。
您可以使用专业化而不是 SFINAE。为避免太多重复,您可以将公共部分(任何不依赖于 Dim
的部分)移动到基础 class:
#include <type_traits>
template <typename T>
class base_unit {
public:
explicit base_unit(T t): _value(t) {}
T _value;
};
template<typename T, int Dim>
class Unit : public base_unit<T> {
public:
explicit Unit(T t): base_unit<T>(t) {}
};
template <typename T>
class Unit<T,0> : public base_unit<T> {
public:
explicit Unit(T t) : base_unit<T>(t) {}
operator T() { return base_unit<T>::_value; }
};
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
请注意,这有点过时并且没有考虑更现代的 C++20 方法(例如评论中提到的 operator T() requires (Dim == 0)
)。
Do you have an idea for equivalent code that also works in GCC? The code should work in C++17 with both compilers.
由于您不想更改调用端的代码并且问题出在 y+i
,您可以重载 operator+
,如下所示:
#include <type_traits>
#include <iostream>
template<typename T, int Dim>
class Unit {
public:
explicit Unit(T t): _value(t) {}
template<int D = Dim, typename std::enable_if_t<D==0, int> = 0>
operator T() { static_assert(Dim==0, ""); return _value; } //static_assert not necessary, but gives error if template is removed
T _value;
//overload operator+
template<typename U,int D, typename V> friend U operator+( Unit<U,D>& u, V& v);
};
//define overloaded operator+
template<typename U, int D, typename V> U operator+( Unit<U,D>& u, V&v)
{
std::cout<<u.operator U() + v;//just for checking the value
return u.operator U() + v;
}
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
上面程序的输出可见here.
我想要一个 class 代表具有某种维度的单位。这应该表达类似 1.5m^2 的内容。应允许与某种类型进行标量乘法,无量纲单位的行为应与基础类型完全相同。这是我的解决方案:
#include <type_traits>
template<typename T, int Dim>
class Unit {
public:
explicit Unit(T t): _value(t) {}
template<int D = Dim, typename std::enable_if_t<D==0, int> = 0>
operator T() { static_assert(Dim==0, ""); return _value; } //static_assert not necessary, but gives error if template is removed
T _value;
};
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
这在 clang (https://godbolt.org/z/8Pev7W6Y1). However, due to a GCC bug with templated conversion operators (
无法删除 SFINAE 结构,因为它(正确地)遇到了 static_assert
。
您是否有同样适用于 GCC 的等效代码的想法?代码应该在 C++17 中与两个编译器一起工作。
您可以使用专业化而不是 SFINAE。为避免太多重复,您可以将公共部分(任何不依赖于 Dim
的部分)移动到基础 class:
#include <type_traits>
template <typename T>
class base_unit {
public:
explicit base_unit(T t): _value(t) {}
T _value;
};
template<typename T, int Dim>
class Unit : public base_unit<T> {
public:
explicit Unit(T t): base_unit<T>(t) {}
};
template <typename T>
class Unit<T,0> : public base_unit<T> {
public:
explicit Unit(T t) : base_unit<T>(t) {}
operator T() { return base_unit<T>::_value; }
};
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
请注意,这有点过时并且没有考虑更现代的 C++20 方法(例如评论中提到的 operator T() requires (Dim == 0)
)。
Do you have an idea for equivalent code that also works in GCC? The code should work in C++17 with both compilers.
由于您不想更改调用端的代码并且问题出在 y+i
,您可以重载 operator+
,如下所示:
#include <type_traits>
#include <iostream>
template<typename T, int Dim>
class Unit {
public:
explicit Unit(T t): _value(t) {}
template<int D = Dim, typename std::enable_if_t<D==0, int> = 0>
operator T() { static_assert(Dim==0, ""); return _value; } //static_assert not necessary, but gives error if template is removed
T _value;
//overload operator+
template<typename U,int D, typename V> friend U operator+( Unit<U,D>& u, V& v);
};
//define overloaded operator+
template<typename U, int D, typename V> U operator+( Unit<U,D>& u, V&v)
{
std::cout<<u.operator U() + v;//just for checking the value
return u.operator U() + v;
}
template<typename S, typename T, int Dim>
auto operator*(S s, Unit<T,Dim> unit)
{
return Unit<T, Dim>(s * unit._value);
}
auto main() -> int
{
auto i = double{0};
//Scalar test
auto scalar = int{0};
auto x = Unit<double,1>(i);
auto test = scalar * x;
//Conversion test
auto y = Unit<double,0>(i);
return y + i;
}
上面程序的输出可见here.