2

I have an example C++ program (included below) that works in Visual Studio 2019 with a plain version of class1 but not with a template version of class1. Both versions work in gcc. The error message I get from Visual Studio 2019 is:

Severity:    Error
Code:        C2063
Description: 'ns1::operator *': not a function

My question is how does Visual Studio expect me to declare the friend operator for the template case in the example below ?

Version of code with class1 a plan class. This works on gcc and visual Studio 2019:

# include <iostream>

// begin ns1
namespace ns1 {
    // begin class1
     class class1 {
    private:
       int value_;
    public:
       class1(int value ) : value_(value)
       { }
       int Value(void) const
       {   return value_; }
    }; // end class1
    // forward declaration or operator
     class1 operator * 
    (const class1& left, const class1& right);
    // begin ns1
    namespace ns2 {
        // begin class2
        class class2 {
            friend ns1::class1 ns1::operator * 
            (const ns1::class1& left, const ns1::class1& right);
        private:
            int value_;
        public:
            class2( int value ) : value_(value)
            { }
            int Value(void) const
            {   return value_; }
        }; // end class2
    } // end ns2
     class1 operator *
    (const class1& left, const class1 &right)
    {   ns2::class2 one(1);
    // use private access to value_ to make sure friend statement working
    return class1( one.value_ * left.Value() * right.Value() );
    }
} // end ns1

// begin main
int main(void)
{   ns1::class1 two(2), three(3);
    ns1::class1 six = two * three;
    std::cout << "six.Value() = " << six.Value() << "\n";
    return 0;
} // end main

Version of code with class1 a template class. This works on gcc but not in Visual Studio 2019. I am using a modified version of the Hello World console example in Visual Studio.

# include <iostream>

// begin ns1
namespace ns1 {
    // begin class1
    template <class Type> class class1 {
    private:
       Type value_;
    public:
       class1(Type value ) : value_(value)
       { }
       Type Value(void) const
       {   return value_; }
    }; // end class1
    // forward declaration or operator
    template <class Type> class1<Type> operator * 
    (const class1<Type>& left, const class1<Type>& right);
    // begin ns1
    namespace ns2 {
        // begin class2
        class class2 {
            friend ns1::class1<int> ns1::operator * <int> 
            (const ns1::class1<int>& left, const ns1::class1<int>& right);
        private:
            int value_;
        public:
            class2( int value ) : value_(value)
            { }
            int Value(void) const
            {   return value_; }
        }; // end class2
    } // end ns2
    template <class Type> class1<Type> operator *
    (const class1<Type>& left, const class1<Type> &right)
    {   ns2::class2 one(1);
    // use private access to value_ to make sure friend statement working
    return class1<Type>( one.value_ * left.Value() * right.Value() );
    }
} // end ns1

// begin main
int main(void)
{   ns1::class1<int> two(2), three(3);
    ns1::class1<int> six = two * three;
    std::cout << "six.Value() = " << six.Value() << "\n";
    return 0;
} // end main
Brad Bell
  • 21
  • 3
  • 2
    Can you remove macros from the code please? It's hard to read. :| – HolyBlackCat Aug 06 '20 at 17:03
  • 1
    Can't reproduce, the shown code compiles with no errors or warnings. – anastaciu Aug 06 '20 at 17:06
  • 1
    @anastaciu Same here, on `g++-10` with `-std=c++17 -Wall -Wextra -Wpedantic` – underscore_d Aug 06 '20 at 17:10
  • 1
    Success on [godbolt](https://godbolt.org/z/1z3eEY) too with the 3 compilers. – Jarod42 Aug 06 '20 at 17:11
  • 1
    @underscore_d, it's the default behavior of MSVC, yes :) but in this case I did compile it with MSVC, so at least for this one we can't blame it. – anastaciu Aug 06 '20 at 17:12
  • are you sure it's compiler error and not IntelliSense error\bug? It can e confused by macro-definitions just like humans can be. compiles on MSVC2017. Tbh, this is style of code decoration I would chew my students for – Swift - Friday Pie Aug 06 '20 at 17:21
  • As requested above, I modified the code to be two separate examples instead of one example with macro definitions for the two cases. – Brad Bell Aug 07 '20 at 14:17

3 Answers3

1

This:

# include <iostream>

namespace ns1 {
  template <class Type> class class1 {
    private:
      Type value_;
    public:
      class1(Type value ) : value_(value) { }
      Type Value(void) const { return value_; }
  };

  // forward declaration or operator
  template <class Type> class1<Type> operator * (const class1<Type>& left, const class1<Type>& right);

  namespace ns2 {
    class class2 {
      friend class1<int> operator * <int> (const class1<int>& left, const class1<int>& right);
      private:
        int value_;
      public:
        class2( int value ) : value_(value) { }
        int Value(void) const { return value_; }
    };
  }
    
  template <class Type> class1<Type> operator * (const class1<Type>& left, const class1<Type> &right) {
    ns2::class2 one(1);
    // use private access to value_ to make sure friend statement working
    return class1<Type>( one.value_ * left.Value() * right.Value() );
  }
}

int main(void) {
  ns1::class1<int> two(2), three(3);
  ns1::class1<int> six = two * three;
  std::cout << "six.Value() = " << six.Value() << "\n";
  return 0;
}

works with g++:

gcc -std=c++14 -Wall -pedantic -c b.cpp

and breaks with Visual Studio 2019 version 19.27 with error C2063: 'ns1::operator *': non è una funzione at the friend declaration.

This diff fixes it:

<       friend class1<int> operator * <int> (const class1<int>& left, const class1<int>& right);
---
>       template <class Type> friend class1<Type> ns1::operator * (const class1<Type>& left, const class1<Type>& right);

Note: this only happens with operator *, if I try operator + there is no need to use the generic template ... weird !

paolog
  • 11
  • 1
1

I've boiled it down to a simple reproduction case.

namespace Some
{

// Forward declarations.
template <typename T> class Stuff;
template <typename T> class Thing;
template <typename T> Stuff<T> operator*( const Stuff<T>& lhs, const Thing<T>& rhs );
template <typename T> Thing<T> operator*( const T& s, const Thing<T>& rhs );

// Class definition.
template <typename T>
class Thing
{
#if 0
    template<typename U> friend typename std::enable_if<std::is_same<T, U>::value, Stuff<U> >::type operator*(const Stuff<U>& lhs, const Thing<U>& rhs);
    template<typename U> friend typename std::enable_if<std::is_same<T, U>::value, Thing<U> >::type operator*(const U& s, const Thing<U>& rhs);
#else
    friend Stuff<T> Some::operator* <T>( const Stuff<T>& lhs, const Thing<T>& rhs );
    friend Thing<T> Some::operator* <T>( const T& s, const Thing<T>& rhs );
#endif
};

} //namespace Some

int
main(int argc, char** argv)
{
    Some::Thing<float> mat;
    return 0;
}

This builds fine with VS2010, VS2017, gcc/MinGW, and clang/MinGW, but VS2019 generates the bogus error.

I've filed this as a bug with Microsoft.

Incidentally, the commented-out version works across all compilers too, and ensures that the friend function is only defined for the same type, but is obviously a lot more wordy.

ulatekh
  • 1,311
  • 1
  • 14
  • 19
  • 1
    This fails with msvc19.28, 19.30 but is fixed in 19.33 https://godbolt.org/z/8fcqabn6q – kebs Dec 15 '22 at 13:29
0

Reproducible here:

cl a.cpp
Microsoft (R) C/C++ Optimizing Compiler versione 19.27.29111 per x64
Copyright (C) Microsoft Corporation. Tutti i diritti  sono riservati.

a.cpp
a.cpp(22): error C2063: 'ns1::operator *': non è una funzione

maybe it works on godbolt because it has 19.24 ?

paolog
  • 1