This is kind of a followup question to my previous one (Unified way for checking the existence of member functions, free functions and operators).
I am trying to generate operators for scalar * vector
and vector * scalar
only if they are not provided by the vector class at hand.
I check if those operators exist in the following way:
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;
};
For vectors from the Eigen library (version 3.3.4) this produces:
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
which is fine. However, as soon as I try to declare the righthandside multiplication operator:
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
}
the output changes to
has_operator_mult<Vector3f, Vector3f, float>::value; // true -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // false -> Vector3f = float * Vector3f
So the operator that I am not declaring myself somhow vanishes according to my existence check. Interestingly this does not change whether I use
std::enable_if_t
<
!has_operator_mult<TVector3D, TVector3D, float>::value // with negation
>
or
std::enable_if_t
<
has_operator_mult<TVector3D, TVector3D, float>::value // without negation
>
Both operators however are still callable, but call the implementations of the Eigen library.
Vector3f v(1, 2, 3);
std::cout << v * 5.f << std::endl << std::endl;
std::cout << 5.f * v << std::endl << std::endl;
When I add the other multiplication operator, things get even more confusing:
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
Why is this happening? Is the check for the existence of the operator wrong? It seems to work as long as I am not declaring operators myself.
With gcc and clang your code snippets work fine, but not with MSVC. So I recommend you to report the issue to the MSVC team.
You can try by yourself with the three compilers there.