2

I have read this question and its answer and still don't understand why I am encountering this issue.

This code compiles in VS2017:

#include <iostream>
#include <string>

template <class T>
struct A
{
    template<class U>
    friend std::enable_if_t<!std::is_void<U>::value, A<void>> operator&&(A<U> a1, A<void> a2)
#if 1
    ;
#else
    {
        return {};
    }
#endif

};

#if 1
template<class U>
std::enable_if_t<!std::is_void<U>::value, A<void>> operator&&(A<U> a1, A<void> a2)
{
    return {};
}
#endif

int main()
{
    std::string s;
    A<int> a1;
    A<void> a2, a3;
    
    a3 = a1 && a2;
    
    std::cout << "Press ENTER to exit.\n";
    std::getline(std::cin, s);
}

But if I change the #if 1s to #if 0s, I get C2995: template function has already been defined.

Why does the compilation fail if I define the friend function inside struct A but succeed if I define it outside?

TRPh
  • 187
  • 1
  • 9
  • perhaps there is a "1" template already in the global? Try changing the "1" to something unique and see if that gives the same results. – J'e Feb 22 '21 at 17:13

1 Answers1

3

There is actually a rule in the standard that says this is an error, from [temp.inst.3]:

However, for the purpose of determining whether an instantiated redeclaration is valid according to [basic.def.odr] and [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition.

Example from the standard:

template<typename T> struct Friendly {
  template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff;  // error: produces second definition of f(U)

In your example, A<void> is instantiated when the friend function is instantiated in the instantiation of A<int>. That generates the second definition of the friend function.

ph3rin
  • 4,426
  • 1
  • 18
  • 42