为什么有条件地编译一个运算符模板会改变另一个运算符的可用性?
Why does conditionally compiling an operator template change the availability of another operator?
这是我上一个问题 () 的后续问题。
我正在尝试为 scalar * vector
和 vector * scalar
生成运算符,前提是它们不是由手头的向量 class 提供的。
我通过以下方式检查这些运算符是否存在:
template<typename C, typename Ret, typename Arg>
struct has_operator_mult {
private:
template<typename L, typename R, std::enable_if_t<
std::is_convertible
<
decltype(std::declval<L>() * std::declval<R>()),
Ret
>::value
> * = nullptr >
static constexpr std::true_type check(nullptr_t);
template<typename, typename>
static constexpr std::false_type check(...);
public:
typedef decltype(check<C, Arg>(nullptr)) type;
static constexpr bool value = type::value;
};
对于来自 Eigen 库(版本 3.3.4)的向量,这会产生:
has_operator_mult<Vector3f, Vector3f, float>::value; // true -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // true -> Vector3f = float * Vector3f
has_operator_mult<Vector3f, Vector3f, std::vector<float>>::value // false
很好。但是,一旦我尝试声明右手边的乘法运算符:
template<typename TVector3D,
std::enable_if_t
<
!has_operator_mult<TVector3D, TVector3D, float>::value
> * = nullptr
>
TVector3D operator*(const TVector3D & lhs, float rhs)
{
return TVector3D{}; // implementation does not matter here
}
输出变为
has_operator_mult<Vector3f, Vector3f, float>::value; // true -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // false -> Vector3f = float * Vector3f
所以根据我的存在检查,我没有声明自己的操作员以某种方式消失了。
有趣的是,这不会改变我是否使用
std::enable_if_t
<
!has_operator_mult<TVector3D, TVector3D, float>::value // with negation
>
或
std::enable_if_t
<
has_operator_mult<TVector3D, TVector3D, float>::value // without negation
>
不过,这两个运算符仍然可以调用,但会调用 Eigen 库的实现。
Vector3f v(1, 2, 3);
std::cout << v * 5.f << std::endl << std::endl;
std::cout << 5.f * v << std::endl << std::endl;
当我添加另一个乘法运算符时,事情变得更加混乱:
template<typename TVector3D,
std::enable_if_t
<
!has_operator_mult<float, TVector3D, TVector3D>::value
> * = nullptr
>
TVector3D operator*(float lhs, const TVector3D & rhs)
{
return TVector3D{};
}
has_operator_mult<Vector3f, Vector3f, float>::value; // false -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // true -> Vector3f = float * Vector3f
Vector3f v(1, 2, 3);
std::cout << v * 5.f << std::endl << std::endl; // calls my implementation
std::cout << 5.f * v << std::endl << std::endl; // calls Eigen implementation
std::cout << v * 5 << std::endl << std::endl; // compiler error, ambiguous function call
std::cout << 5 * v << std::endl << std::endl; // calls Eigen implementation
为什么会这样?检查操作员是否存在错误?只要我不自己声明运算符,它似乎就可以工作。
使用 gcc 和 clang,您的代码片段可以正常工作,但不适用于 MSVC。所以我建议你向 MSVC 团队报告这个问题。
三个编译器你可以自己试试there.
这是我上一个问题 (
我正在尝试为 scalar * vector
和 vector * scalar
生成运算符,前提是它们不是由手头的向量 class 提供的。
我通过以下方式检查这些运算符是否存在:
template<typename C, typename Ret, typename Arg>
struct has_operator_mult {
private:
template<typename L, typename R, std::enable_if_t<
std::is_convertible
<
decltype(std::declval<L>() * std::declval<R>()),
Ret
>::value
> * = nullptr >
static constexpr std::true_type check(nullptr_t);
template<typename, typename>
static constexpr std::false_type check(...);
public:
typedef decltype(check<C, Arg>(nullptr)) type;
static constexpr bool value = type::value;
};
对于来自 Eigen 库(版本 3.3.4)的向量,这会产生:
has_operator_mult<Vector3f, Vector3f, float>::value; // true -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // true -> Vector3f = float * Vector3f
has_operator_mult<Vector3f, Vector3f, std::vector<float>>::value // false
很好。但是,一旦我尝试声明右手边的乘法运算符:
template<typename TVector3D,
std::enable_if_t
<
!has_operator_mult<TVector3D, TVector3D, float>::value
> * = nullptr
>
TVector3D operator*(const TVector3D & lhs, float rhs)
{
return TVector3D{}; // implementation does not matter here
}
输出变为
has_operator_mult<Vector3f, Vector3f, float>::value; // true -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // false -> Vector3f = float * Vector3f
所以根据我的存在检查,我没有声明自己的操作员以某种方式消失了。 有趣的是,这不会改变我是否使用
std::enable_if_t
<
!has_operator_mult<TVector3D, TVector3D, float>::value // with negation
>
或
std::enable_if_t
<
has_operator_mult<TVector3D, TVector3D, float>::value // without negation
>
不过,这两个运算符仍然可以调用,但会调用 Eigen 库的实现。
Vector3f v(1, 2, 3);
std::cout << v * 5.f << std::endl << std::endl;
std::cout << 5.f * v << std::endl << std::endl;
当我添加另一个乘法运算符时,事情变得更加混乱:
template<typename TVector3D,
std::enable_if_t
<
!has_operator_mult<float, TVector3D, TVector3D>::value
> * = nullptr
>
TVector3D operator*(float lhs, const TVector3D & rhs)
{
return TVector3D{};
}
has_operator_mult<Vector3f, Vector3f, float>::value; // false -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // true -> Vector3f = float * Vector3f
Vector3f v(1, 2, 3);
std::cout << v * 5.f << std::endl << std::endl; // calls my implementation
std::cout << 5.f * v << std::endl << std::endl; // calls Eigen implementation
std::cout << v * 5 << std::endl << std::endl; // compiler error, ambiguous function call
std::cout << 5 * v << std::endl << std::endl; // calls Eigen implementation
为什么会这样?检查操作员是否存在错误?只要我不自己声明运算符,它似乎就可以工作。
使用 gcc 和 clang,您的代码片段可以正常工作,但不适用于 MSVC。所以我建议你向 MSVC 团队报告这个问题。
三个编译器你可以自己试试there.