Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does public overload conflict with private using directive on some compilers?

I came across the following situation in one of my projects, where a base class has a function template, which is hidden by a non-templated function in a derived class template. Further down the class hierarchy, the non-templated function is explicitly bringing the function into the scope via a using directive.

Here's a simplified example code:

class Base
{
public:
  template<typename T> const T& get() const;
};

template<typename T> class Derived : public Base
{
private:
  using Base::get;
public:
  const T& get() const;
};

template<typename T> class MoreDerived : public Derived<T>
{
public:
  using Derived<T>::get; // <-- this causes the problem

  const T& call_get() {
    return get();
  }
};

template class MoreDerived<int>;

Godbolt: https://godbolt.org/z/5MQ0VL

The above code fails on GCC and Clang with errors like:

<source>:15:28: error: 'template<class T> const T& Base::get() const' is inaccessible within this context

   15 | template<typename T> class MoreDerived : public Derived<T>

MSVC and ICC accept this code without complaints.

I'm wondering, why the compilers complain about Base::get<T> while there is a public overload Derived<T>::get available?

Removing the private using Base::get from Derived<T> leads to warnings about hiding functions from the base class. So this is unfortunately also not an ideal option.

Without the using Derived<T>::get, calls to get have to be qualified within MoreDerived as it would not be a dependent name otherwise.

Any ideas, what I'm doing wrong here?

like image 508
pah Avatar asked May 22 '19 07:05

pah


1 Answers

I believe what applies here is [namespace.udecl]/17:

In a using-declarator that does not name a constructor, all members of the set of introduced declarations shall be accessible. In a using-declarator that names a constructor, no access check is performed. In particular, if a derived class uses a using-declarator to access a member of a base class, the member name shall be accessible. If the name is that of an overloaded member function, then all functions named shall be accessible. […]

(emphasis mine) in combination with [namespace.udecl]/19:

A synonym created by a using-declaration has the usual accessibility for a member-declaration. […]

The using declaration in MoreDerived creates a synonym for Derived::get which itself is a synonym for the overload set consisting of the member function Derived::get and the member function template Base::get. The latter is not accessible at the point of the using declaration in MoreDerived (because it is private in Derived). Thus, GCC and Clang are correct, this code should not compile. Moving the using declaration in Derived from the private to the public part, for example

template<typename T> class Derived : public Base
{
public:
  using Base::get;
  const T& get() const;
};

resolves the issue…

like image 96
Michael Kenzel Avatar answered Oct 06 '22 09:10

Michael Kenzel