简体   繁体   中英

Friend template operator

The following program does not compile in MS Visual Studio 19.

#include <iostream>
#include <string>

template <typename T>
class A;

template <typename T>
std::ostream &operator <<( std::ostream &, const A<T> & );

template <typename T>
class A
{
private:
    T x;

public:
    A( const T &x ) : x( x ) {}

    friend std::ostream &::operator <<( std::ostream &, const A<T> & );
};

template <typename T>
std::ostream &operator <<( std::ostream &os, const A<T> &a )
{
    return os << "a.x = " << a.x;
}

int main()
{
    std::cout << A<std::string>( "Hello" ) << '\n';
}

The compiler says that operator << is not a function.

While the following program

#include <iostream>
#include <string>

template <typename T>
class A;

template <typename T>
std::ostream &f( std::ostream &, const A<T> & );

template <typename T>
class A
{
private:
    T x;

public:
    A( const T &x ) : x( x ) {}

    friend std::ostream &::f( std::ostream &, const A<T> & );
};

template <typename T>
std::ostream &f( std::ostream &os, const A<T> &a )
{
    return os << "a.x = " << a.x;
}

int main()
{
    f( std::cout, A<std::string>( "Hello" ) ) << '\n';
}

compiles successfully.

What is the reason of that the first program does not compile? Is it a bug of MS Visual Studio 19 or do I have missed something from the C++ 20 Standard?

According to the C++ 20 Standard (13.7.4 Friends)

(1.3) — if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template (13.10.2.6), otherwise,

Your program is defective; however there is indeed a bug (or two) in Visual Studio.

First, your friend declaration should be spelled with a <> to indicate that it is a template specialization that is the friend:

friend std::ostream &::operator <<<>( std::ostream &, const A<T> & );
//                                ^~

It is also acceptable to spell it with T explicitly (ie not inferring the template arguments):

friend std::ostream &::operator <<<T>( std::ostream &, const A<T> & );
//                                ^~~

Incidentally, gcc provides a helpful hint to make this change.

However MSVC will still erroneously reject the program; this appears to only happen templates of the specific operator<< where the class template argument is passed by reference, but not by value:

#include <iostream>
template<class> struct A;
template<class> struct B;
template<class T> std::ostream& f(std::ostream&, A<T>);
template<class T> std::ostream &operator<<(std::ostream&, A<T>);
template<class T> std::ostream& f(std::ostream&, B<T> const&);
template<class T> std::ostream &operator<<(std::ostream&, B<T> const&);
template<class T> struct A {
    friend std::ostream& ::f<T>(std::ostream&, A<T>);
    friend std::ostream& ::operator<<<T>(std::ostream&, A<T>);
};
template<class T> struct B {
    friend std::ostream& ::f<T>(std::ostream&, B<T> const&);
    friend std::ostream& ::operator<<<T>(std::ostream&, B<T> const&); // MSVC bug
};
template<class T> std::ostream& f(std::ostream& os, A<T>) { return os << "1"; }
template<class T> std::ostream& operator<<(std::ostream& os, A<T>) { return os << "2"; }
template<class T> std::ostream& f(std::ostream& os, B<T> const&) { return os << "3"; }
template<class T> std::ostream& operator<<(std::ostream& os, B<T> const&) { return os << "4"; }
int main() {
    f(std::cout, A<int>()) << ' ' << A<int>() << '\n';
    f(std::cout, B<int>()) << ' ' << B<int>() << '\n';
}

The fact that MSVC rejects only friend std::ostream&::operator<<<T>(std::ostream&, B<T> const&) makes it clear that this is a compiler bug. gcc and clang accept and print 1 2\n3 4 , as expected.

Another, similar defect in MSVC is that it will reject friend std::ostream&::f<>(std::ostream&, A) (that is, using the injected-class-name to name A<T> ).

Regarding the Standard, the language was changed substantially post-C++20 by P1787R6: Declarations and where to find them ; in particular, it removes the language that you quote in your question. As such, I don't think you can use that as an argument for being allowed to omit the <> from the friend function template specialization declaration, since P1787R6 resolves a bunch of CWG DRs and so can be regarded as a post-publication correction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM