10

I recently encountered an interesting C++ code example that compiles with Clang (-std=c++20) but fails with GCC and MSVC. The code involves operator overloading and friend function declarations.

Code Example:

#include <iostream>

template<typename T>
class Foo;

template<typename T>
Foo<T> operator+(int, const Foo<T>&);

template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
    Foo operator+(int);

    friend Foo<T> operator+<>(int, const Foo<T>&);

private:
    T data;
};

template<typename T>
Foo<T> Foo<T>::operator+(int num)
{
    return Foo(this->data + num);
}

template<typename T>
Foo<T> operator+(int num, const Foo<T>& other)
{
    return Foo(other.data + num);
}

int main()
{
    Foo<int> a(1);
    auto v1 = a + 2;
    auto v2 = 2 + a;
}

Compiler Behavior: Clang (17.0.1): Works with -std=c++20

GCC (13.2): Fails with error about operator+ not being declared

MSVC (v19.38): Similar failure

GCC has an error:

<source>: In instantiation of 'class Foo<int>':
<source>:35:15:   required from here
   35 |     Foo<int> a(1);
      |               ^
<source>:15:19: error: template-id 'operator+<>' for 'Foo<int> operator+(int, const Foo<int>&)' does not match any template declaration
   15 |     friend Foo<T> operator+<>(int, const Foo<T>&);
      |                   ^~~~~~~~~~~
<source>:22:8: note: candidate is: 'Foo<T> Foo<T>::operator+(int) [with T = int]'
   22 | Foo<T> Foo<T>::operator+(int num)
      |        ^~~~~~
<source>: In instantiation of 'Foo<T> operator+(int, const Foo<T>&) [with T = int]':
<source>:37:19:   required from here
   37 |     auto v2 = 2 + a;
      |                   ^
<source>:30:22: error: 'int Foo<int>::data' is private within this context
   30 |     return Foo(other.data + num);
      |                ~~~~~~^~~~
<source>:18:7: note: declared private here
   18 |     T data;
      |       ^~~~

Workaround: Swapping the order of operator declarations inside the class makes it work:

template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
    friend Foo<T> operator+<>(int, const Foo<T>&);
    Foo operator+(int);

private:
    T data;
};
4
  • Sorry, i've seen this solution but it has nothing with operators and overloading them. Commented May 6 at 5:15
  • 1
    GCC issue submitted: gcc.gnu.org/bugzilla/show_bug.cgi?id=120130 Commented May 6 at 11:39
  • 1
    If I change the friend declaration to be template <typename U> friend auto operator+(int, Foo<U> const&) -> Foo<U>;, then the code works with g++ 14.2.0 and --std=c++20. Commented May 6 at 12:04
  • 1
    template <typename U> friend ... makes all specializations of operator+ to be friends, and OP wants to make only one specialization a friend. So it can be done this way friend auto ::operator+<>(int, const Foo<T>&) -> Foo<T>;, which is accepted everywhere: gcc.godbolt.org/z/jnd8WWE6j Commented May 7 at 9:06

1 Answer 1

1

it seems to be that Foo + operater wasn't instantiated and thus never properly functions for the call. where as you're workaround gives a way to deduce and is actually a template now, where as it can't use the operator as a template and thus never functions due to it never doing anything since operator itself does not provide template functionality.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.