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.