issues with class friendship and inheritance - c++

I feel like I'm missing something obvious here, but I have a class that declares another class as a friend yet does not seem to have access to its private members.
I have minimized the issue as follows:
Widget.hpp:
template<typename T> class Foo;
template<typename T, typename U>
class WidgetBase
{
protected:
T* ptr;
public:
WidgetBase(T* ptr) : ptr{ptr} {}
virtual void f() = 0;
};
template<typename T>
class WidgetDerived : public WidgetBase<typename Foo<T>::Bar, T>
{
public:
using WidgetBase<typename Foo<T>::Bar, T>::WidgetBase;
using WidgetBase<typename Foo<T>::Bar, T>::ptr;
virtual void f() { ptr->x = 9; }
};
Foo.hpp:
#include "Widget.hpp"
template<typename T>
class Foo
{
private:
struct Bar
{
T x;
explicit Bar(T x) : x{x} {}
};
public:
Bar* bar;
explicit Foo(T x) : bar{new Bar{x}} {}
~Foo() { delete bar; }
WidgetDerived<T> foo_widget() { WidgetDerived<T> widget{bar}; return widget; }
friend class WidgetBase<Bar, T>;
friend class WidgetDerived<T>;
};
main.cpp:
#include "Foo.hpp"
int main()
{
Foo<int> foo(7);
auto widget = foo.foo_widget();
}
Using GCC 7.3.0, I attempt to compile with: g++ -o main main.cpp -pedantic -Wall -Werror -Wconversion -std=c++1z and receive the following error message:
In file included from Foo.hpp:1:0,
from main.cpp:1:
Widget.hpp: In instantiation of 'class WidgetDerived<int>':
main.cpp:6:34: required from here
Widget.hpp:14:7: error: 'struct Foo<int>::Bar' is private within this context
class WidgetDerived : public WidgetBase<typename Foo<T>::Bar, T>
^~~~~~~~~~~~~
In file included from main.cpp:1:0:
Foo.hpp:7:12: note: declared private here
struct Bar
^~~
Note that I declare both WidgetBase and WidgetDerived as friends of Foo. What am I missing here?

This is a GCC (G++) bug, see bug report. The code compiles just fine with clang 6.0.0. However, clang 5.0.0 also reject the code.
Unfortunately I can only suggest these two possible solutions. 1) make the struct public, or 2) compile with a recent version of clang.

Related

expose typedef for function in cpp for template class

this code compiled fine with gcc
foo.h
template<typename T>
class Base
{
static const int n = 10;
};
template<typename T>
class Child : public Base
{
typedef Base<T> base_t;
void Compute(Matrix<base_t::n, base_t::n>& M);
}
foo.cpp
template<typename T>
Child<T>::Compute(Matrix<base_t::n, base_t::n>& M)
{}
But with VS2019, it gaves error C2244: unable to match function definition to an existing declaration. It works if in cpp one defines
Child<T>::Compute(Matrix<Child<T>::base_t::n, Child<T>::base_t::n>& M)
But this looks ugly. Nevertheless why gcc could infer the base_t at the first place? Is there a flag with VS that makes it behave the same as gcc in this aspect`?

Template member of a non-template class as a friend

The following snippet is compiled fine by gcc, icc and msvc (latest question), but trips clang with <source>:6:9: error: calling a private constructor of class 'B<int>' in the marked line. Yet it works fine for the free template function, as shown in the code:
struct A {
template<class T>
static void create () {
T();
}
};
template<class T>
void create() {
T();
}
template<typename T>
struct B {
friend void A::create<B>();
friend void create<B>();
private:
B() = default;
};
int main() {
A::create<B<int>>(); // clang trips here
create<B<int>>(); // fine!
}
What might be the difference between a static template member of non-template class and free template function in this context?
I found a bug reported for Clang, "Access to a public template alias declaration that refers to friend's private nested type is not allowed" which seems similar to this issue as it relates to a friend (template within a structure as in the OP) accessing a private member of a class.
The failed test case is:
struct FooAccessor
{
template <typename T>
using Foo = typename T::Foo;
};
class Type
{
friend struct FooAccessor;
using Foo = int;
};
int main()
{
FooAccessor::Foo<Type> t;
}
The result is:
$ clang++ test.cpp -std=c++11
test.cpp:4:5: error: 'Foo' is a private member of 'Type'
using Foo = typename T::Foo;
^
test.cpp:11:11: note: implicitly declared private here
using Foo = int;
^
1 error generated.

Visibility of inner template class [duplicate]

I don't understand why in the following code, I am allowed to create the function print_private_template while the compiler complains about print_private_class:
#include <cstdio>
class A
{
private:
template <unsigned T>
struct B
{
};
struct C
{
};
public:
template <unsigned T>
B<T> getAb()
{
return B<T>();
}
C getAc()
{
return C();
}
};
template<unsigned T>
void print_private_template(const A::B<T> &ab)
{
printf("%d\n", T);
}
void print_private_class(const A::C &ac)
{
printf("something\n");
}
int main(int, char**)
{
A a;
print_private_template(a.getAb<42>());
print_private_class(a.getAc());
return 0;
}
Is this an expected behaviour? a compiler bug/extension?
Just to be clear, my goal is to make the compiler error on both the usage of print_private_template and print_private_class.
Comeau does give an error (when you comment out the print_private_class function and its call in strict C++03 mode.
ComeauTest.c(31): error: class template "A::B" (declared at line 7) is inaccessible
void print_private_template(const A::B &ab)
^
detected during instantiation of "print_private_template" based on
template argument <42U> at line 45
G++ 4.5 on Windows does not report any error with -std=c++ -Wall -pedantic though.
Your class A::C and class template A::B<T> both have the same visibility as any other normal members. Hence, both print_private_class and print_private_template require a diagnostic.
11.8 Nested classes [class.access.nest]
1 A nested class is a member and as such has the same access rights as any other member. The members of
an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11)
shall be obeyed.
As stated by Dirk Gently, GCC doesn't perform access control when instantiating template structs / classes nested in other (template) structs / classes.
One way to work around this is to encapsulate them in a non-template struct:
template<int I> class MyTemplate
{
struct PT
{
template<int, typename = void> struct InnerTemplate;
// ... specialisations here ...
};
public:
typedef typename PT::template InnerTemplate<I>::SomeType SomeType;
};
typedef MyTemplate<1>::PT::InnerTemplate<1> ThisWontWork;
The last line will fail to compile with the error:
error: 'struct MyTemplate<1>::PT' is private within this context
I'll grant that this is ugly, especially having to use PT::template but it seems to effectively prevent clients from instantiating helper templates they aren't meant to access, so it's worth a shot.
It got fixed for GCC 11
Ten years later... and the bug got fixed for GCC 11: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437#c13 (another point in the dupe pool had been previously linked to by dirkgently).
A minimal reproduction:
main.cpp
class Out {
protected:
class In {};
};
template <class C>
void f() { Out::In in; }
int main() {
f<Out>();
}
still compiles in GCC 10.2 with all warnings enabled:
g++-10 -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
but fails correctly in clang++ 10:
void f() { Out::In in; }
^
main.cpp:3:11: note: declared protected here
class In {};
^
1 error generated
The above fails to fail in GCC because f is a template function.

How to distingush between template class and implicit template argument inside class defintion? [duplicate]

I have had problems (possibly mine) with template template parameters and clang. The following toy example compiles and runs under g++ 4.7.0, not clang++ 3.0 (based on LLVM 3.0), both ubuntu 12.04.
Toy example (test_1.cpp):
#include <iostream>
#include <memory>
struct AFn
{
void operator()()
{
; // do something
}
};
template<typename T>
struct impl
{
T *backpointer_;
};
template<typename S, template <typename> class T>
struct implT
{
T<S> *backpointer_;
};
template<typename>
class AClass;
template<>
struct implT<AFn, AClass>
{
implT(std::string message) :
message_(message)
{}
void operator()()
{
std::cout << " : " << message_ << std::endl;
}
std::string message_;
};
template<typename Fn>
class AClass
{
private:
std::shared_ptr<implT<Fn, AClass> > p_;
public:
AClass(std::string message) :
p_(std::make_shared<implT<Fn, AClass> >(message))
{}
void call_me()
{
p_->operator()();
}
};
int main(int argc, char **argv)
{
AClass<AFn> *A = new AClass<AFn>("AClass<AFn>");
A->call_me();
delete A;
return 0;
}
clang output:
*****#ely:~$ clang++ -std=c++11 test_1.cpp -o test_1
test_1.cpp:47:30: error: template argument for template template parameter must be a class template or
type alias template
std::shared_ptr<implT<Fn, AClass> > p_;
^
test_1.cpp:47:40: error: C++ requires a type specifier for all declarations
std::shared_ptr<implT<Fn, AClass> > p_;
^~
test_1.cpp:50:36: error: template argument for template template parameter must be a class template or
type alias template
p_(std::make_shared<implT<Fn, AClass> >(message))
^
3 errors generated.
I can't make sense of the first error. It compiles and runs fine with gcc/g++ 4.7.0. Any help would be appreciated.
As noted, it's a Clang bug. AClass there is an injected-class-name, a unique grammatical construct which is both a class-name and a template-name.
Another workaround is to say AClass::template AClass. This avoids needing to qualify AClass with its enclosing namespace.
The same thing happens for me with Clang 3.3.
The solution -- or workaround -- from this SO question is to replace AClass with ::AClass on lines 47 and 50, and then it compiles happily.
To be honest template template parameters make my head hurt. The referenced question suggests it's a Clang bug, but I'm not enough of an expert to be able to say.

template template parameters and clang

I have had problems (possibly mine) with template template parameters and clang. The following toy example compiles and runs under g++ 4.7.0, not clang++ 3.0 (based on LLVM 3.0), both ubuntu 12.04.
Toy example (test_1.cpp):
#include <iostream>
#include <memory>
struct AFn
{
void operator()()
{
; // do something
}
};
template<typename T>
struct impl
{
T *backpointer_;
};
template<typename S, template <typename> class T>
struct implT
{
T<S> *backpointer_;
};
template<typename>
class AClass;
template<>
struct implT<AFn, AClass>
{
implT(std::string message) :
message_(message)
{}
void operator()()
{
std::cout << " : " << message_ << std::endl;
}
std::string message_;
};
template<typename Fn>
class AClass
{
private:
std::shared_ptr<implT<Fn, AClass> > p_;
public:
AClass(std::string message) :
p_(std::make_shared<implT<Fn, AClass> >(message))
{}
void call_me()
{
p_->operator()();
}
};
int main(int argc, char **argv)
{
AClass<AFn> *A = new AClass<AFn>("AClass<AFn>");
A->call_me();
delete A;
return 0;
}
clang output:
*****#ely:~$ clang++ -std=c++11 test_1.cpp -o test_1
test_1.cpp:47:30: error: template argument for template template parameter must be a class template or
type alias template
std::shared_ptr<implT<Fn, AClass> > p_;
^
test_1.cpp:47:40: error: C++ requires a type specifier for all declarations
std::shared_ptr<implT<Fn, AClass> > p_;
^~
test_1.cpp:50:36: error: template argument for template template parameter must be a class template or
type alias template
p_(std::make_shared<implT<Fn, AClass> >(message))
^
3 errors generated.
I can't make sense of the first error. It compiles and runs fine with gcc/g++ 4.7.0. Any help would be appreciated.
As noted, it's a Clang bug. AClass there is an injected-class-name, a unique grammatical construct which is both a class-name and a template-name.
Another workaround is to say AClass::template AClass. This avoids needing to qualify AClass with its enclosing namespace.
The same thing happens for me with Clang 3.3.
The solution -- or workaround -- from this SO question is to replace AClass with ::AClass on lines 47 and 50, and then it compiles happily.
To be honest template template parameters make my head hurt. The referenced question suggests it's a Clang bug, but I'm not enough of an expert to be able to say.