#1009081 g++-11: friend statement not used for template specialization

Package:
g++-11
Source:
gcc-11
Description:
GNU C++ compiler
Submitter:
Vincent Danjean
Date:
2022-04-07 09:21:11 UTC
Severity:
normal
#1009081#5
Date:
2022-04-07 01:44:27 UTC
From:
To:
Contrary to clang++, g++ does not honor "friend" statement when evaluating
template specialization

Here is a short example:
$ cat toto6.cpp
#include <iostream>

struct X {
  template <class T, class U = void> struct check_is_map {
      static constexpr bool value = false;
  };

  template <class T>
  struct check_is_map<T, typename T::is_map> {
      static constexpr bool value = true;
  };
};

template <class T, class U = void> struct check_is_map {
    static constexpr bool value = false;
};

template <class T>
struct check_is_map<T, typename T::is_map> {
    static constexpr bool value = true;
};

struct Config {
    using is_map=void;
};

struct ConfigBis {
  private:
    template <typename T1, typename U1>
    friend struct check_is_map; // friend of the template
    friend struct X; // friend of the surrounding class
    using is_map=void;
};

int main()
{
	std::cerr << "VALUE (public): "
            << check_is_map<Config>::value
                  << std::endl;
	std::cerr << "VALUE (private+friend): "
            << check_is_map<ConfigBis>::value
                  << std::endl;
	std::cerr << "X::VALUE (public): "
            << X::check_is_map<Config>::value
                  << std::endl;
	std::cerr << "X::VALUE (private+friend): "
            << X::check_is_map<ConfigBis>::value
                  << std::endl;
}

$ clang++ -std=gnu++17 -Wall -Wextra toto6.cpp
$ ./a.out
VALUE (public): 1
VALUE (private+friend): 1
X::VALUE (public): 1
X::VALUE (private+friend): 1
$ g++ -std=gnu++17 -Wall -Wextra toto6.cpp
$ ./a.out
VALUE (public): 1
VALUE (private+friend): 0
X::VALUE (public): 1
X::VALUE (private+friend): 0

  Regards,
    Vincent

#1009081#10
Date:
2022-04-07 09:11:37 UTC
From:
To:
   Hi,

   More strange: by adding an unused static method, the behavior
of the previous templates change.

$ cat toto6.cpp
#include <iostream>

struct X {
   template <class T, class U = void> struct check_is_map {
       static constexpr bool value = false;
   };

   template <class T>
   struct check_is_map<T, typename T::is_map> {
       static constexpr bool value = true;
   };

   template <class T> struct check_is_map2 {
     static constexpr bool value = check_is_map<T>::value;
   };

   static void func();
};

template <class T, class U = void> struct check_is_map {
     static constexpr bool value = false;
};

template <class T>
struct check_is_map<T, typename T::is_map> {
     static constexpr bool value = true;
};

struct Config {
     using is_map=void;
};

struct ConfigBis {
   private:
     template <typename T1, typename U1>
     friend struct check_is_map;
     friend struct X;
     using is_map=void;
};

#ifdef FOO
void X::func() {
	std::cerr << "* Through method" << std::endl;
	std::cerr << "X::VALUE (public): "
             << X::check_is_map<Config>::value
                   << std::endl;
	std::cerr << "X::VALUE (private+friend): "
             << X::check_is_map<ConfigBis>::value
                   << std::endl;
	std::cerr << "** By proxy template" << std::endl;
	std::cerr << "X::Proxy::VALUE (public): "
             << X::check_is_map2<Config>::value
                   << std::endl;
	std::cerr << "X::Proxy::VALUE (private+friend): "
             << X::check_is_map2<ConfigBis>::value
                   << std::endl;
}
#endif

int main()
{
	std::cerr << "* From main" << std::endl;
	std::cerr << "VALUE (public): "
             << check_is_map<Config>::value
                   << std::endl;
	std::cerr << "VALUE (private+friend): "
             << check_is_map<ConfigBis>::value
                   << std::endl;
	std::cerr << "X::VALUE (public): "
             << X::check_is_map<Config>::value
                   << std::endl;
	std::cerr << "X::VALUE (private+friend): "
             << X::check_is_map<ConfigBis>::value
                   << std::endl;
	std::cerr << "** By proxy template" << std::endl;
	std::cerr << "X::Proxy::VALUE (public): "
             << X::check_is_map2<Config>::value
                   << std::endl;
	std::cerr << "X::Proxy::VALUE (private+friend): "
             << X::check_is_map2<ConfigBis>::value
                   << std::endl;
}
$ g++ -std=gnu++17 -Wall -Wextra toto6.cpp
$ ./a.out
* From main
VALUE (public): 1
VALUE (private+friend): 0
X::VALUE (public): 1
X::VALUE (private+friend): 0
** By proxy template
X::Proxy::VALUE (public): 1
X::Proxy::VALUE (private+friend): 0
$ g++ -std=gnu++17 -Wall -Wextra toto6.cpp -DFOO
$ ./a.out
* From main
VALUE (public): 1
VALUE (private+friend): 0
X::VALUE (public): 1
X::VALUE (private+friend): 1
** By proxy template
X::Proxy::VALUE (public): 1
X::Proxy::VALUE (private+friend): 1

   Regards,
     Vincent