Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: MSC not resolving a template operator (gcc and clang ok)

I have a question about a C++ operator overloading on template class and type which is not correctly resolved by Microsoft Visual C++ while it is accepted by gcc and clang (Linux and macOS).

I suspect that this is a bug in MSVC++ but I would like to get the advice of experts before reporting the bug, in case it could be the opposite (gcc and clang being wrong).

The idea is a template class which must be instantiated with some integer type. We want to define the addition with any other plain integer types, both ways (class + int and int + class).

The minimal code for which I can reproduce the issue is below:

#include <type_traits>

template <typename INT, typename std::enable_if<std::is_integral<INT>::value>::type* = nullptr>
class A
{
public:
    template<typename INT2>
    A operator+(INT2 x) const { return A(); }
};

template <typename INT1, typename INT2>
A<INT2> operator+(INT1 x1, A<INT2> x2) { return x2 + x1; }

int main(int argc, char* argv[])
{
    typedef A<int> B;
    B x, y;
    y = x + 1;  // ok everywhere
    y = 1 + x;  // VC++: error C2677: binary '+': no global operator found which takes type 'B' (or there is no acceptable conversion)
}

In the original code, there are SFINAE constructs everywhere to enforce type checking when necessary (including in the two "+" operators). I have removed all of them when they did not change the compilation error. Only the "enable_if" in the definition of class A is required. Without it, the code compiles ok with MSVC++.

Microsoft Visual Studio 2019, version 16.9.3.

What is wrong here? MSVC++ or gcc/clang?

Thanks for your advices.

like image 858
Thierry Lelegard Avatar asked Dec 13 '25 12:12

Thierry Lelegard


1 Answers

If we're being pedantic, in C++17, non-type template arguments cannot have type void*, see [temp.param]/4.2:

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
. . .
— pointer to object or pointer to function,
. . .

(Note: it has been rectified in C++20).

So it seems something goes wrong in MSVC (the SFINAE succeeds in A itself but causes the lookup of the operator to fail). MSVC doesn't support C++20 fully yet, but it may still be worth reporting the issue.

For more portable code, use SFINAE based on int instead of void*:

template <typename INT, typename std::enable_if<std::is_integral<INT>::value, int>::type = 0>
class A {
   . . .

This compiles OK in MSVC 16.9.

like image 169
rustyx Avatar answered Dec 15 '25 02:12

rustyx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!