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.
Related
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.
Consider the code:
template <class T>
class Bar {
int foobar;
using X = T();
friend X foo;
};
void foo() {
Bar<void> bar;
bar.foobar = 1;
static_cast<void>(bar);
}
int main() {}
Compiles fine in both gcc and clang. But seemingly equivalent code:
template <class T>
class Bar {
int foobar;
friend T foo;
};
void foo() {
Bar<void()> bar;
bar.foobar = 1;
static_cast<void>(bar);
}
int main() {}
causes error in both gcc and clang. How come the template parameter doesn't work here equivalently to alias?
Because T foo is parsed as the declaration of an object, and instantiation of a template cannot change a declaration of an object to a declaration of a function.
C++ standard/[temp.spec]:
If a function declaration acquired its function type through a dependent type (17.7.2.1) without using the
syntactic form of a function declarator, the program is ill-formed.
In other words, why does this compile fine :
template<typename Type>
class A{
public:
void f();
};
class B{
friend void A<int>::f();
};
template<>
void A<int>::f(){
B* var = new B();
}
While this doesn't :
template<typename Type>
class A{
public:
void f();
};
template<typename Type> // B is now a templated class
class B{
friend void A<Type>::f(); // Friending is done using B templated type
};
template<>
void A<int>::f(){
B<int>* var = new B<int>(); // var is now declared using int as its templated type
}
For the second code snippet, compiler (gcc 6.2, no special flags) says:
main.cpp: In instantiation of ‘class B<int>’:
main.cpp:14:28: required from here
main.cpp:9:15: error: prototype for ‘void A<int>::f()’ does not match any in class ‘A<int>’
friend void A<Type>::f();
^~~~~~~
main.cpp:13:6: error: candidate is: void A<Type>::f() [with Type = int]
void A<int>::f(){
As I understand it, in the second code snippet, when declaring var the compiler should parse B class declaration, replace the Type used in the friend declaration by int, and everything should work fine. What am I missing?
EDIT : comments below have pointed out that the second code snippet seems to compile correctly with clang and Visual C++ 2015
An explicit instantiation of B<int> before it is used in A<int>::f() resolves this problem. I assume GCC tries an implicit instantiation of B<int> in the definition of A<int>::f(). But the definition of A<int>::f() is not finished and GCC 'looses' the friend declaration. It looks like a compiler problem.
template<typename Type>
class A
{
public:
void f();
};
template<typename Type> // B is now a templated class
class B
{
friend void A<Type>::f(); // Friending is done using B templated type
};
template
class B<int>; // <= explicit instantiation, that works
template<>
void A<int>::f()
{
B<int>* var = new B<int>();
}
Specializing template class member function without specializing whole template class is special case when you are allowed to specialize non-template member function, so maybe GCC is confused, and I don't know the reasons, but somehow you can't declare friendship to specialized non-template member of template class. Temporary solution would be to specialize whole class template for this to work.
//class template A
template<typename Type>
class A{
public:
void f();
};
//class A<int>
template<>
class A<int>{
public:
void f();
};
Then, define A<int>::f:
For class B:
void A<int>::f(){
B* var = new B();
(void)(var);
}
For template class B:
void A<int>::f(){
B<int>* var = new B<int>();
(void)(var);
}
But I think Clang is right here, there should be no problems for such friend declaration. It's probably a bug in GCC.
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.
This is regarding a test case which involves default argument
deduction/substitution. The test case can be summarized as :
template <class T, class = typename T::I>h(T){}
template <class T, class = typename T::I>j(T){}
class A
{
typedef int I;
friend void h<A>(A);
};
int main()
{
A a;
h(a);
j(a);
}
gcc-4.8.1 throws error for function j, since it has been not been declared friend nor it is private to class A and hence violates access rule for private member I(which is valid). gcc does not throw error for function h since it has been declared as friend to class A and hence can access private member I.
Clang throws error for both the functions. Error for function j (not declared friend is valid and as expected), but it throws error even for friend function h (error : deduction of default arg failed since I is a private member of class A). This violates the accessibility of the friend function.
I checked the code path. While clang is able to deduce the default argument, it checks for access rules before doing any substitution, and gives error. Can someone please provide guidance as to how should this be fixed?
You forgot the return type for the template functions.
This should solve the problem :
template <class T, class = typename T::I> void h(T){}
template <class T, class = typename T::I> void j(T){}
After fixing above errors, I still got errors, because
you declared the typedef in the private section. You need to make it public
you declared function friend wrongly. It has two template arguments (A::I will not work, since A is not complete type)
Fully compilable problem is here :
#include <iostream>
// Type your code here, or load an example.
template <class T, class = typename T::I> void h(T t){std::cout<<t.a<<std::endl;}
template <class T, class = typename T::I> void j(T t){std::cout<<t.a<<std::endl;}
class A
{
friend void h<A,int>(A);
friend void j<A,int>(A);
public :
typedef int I;
private :
int a;
};
int main()
{
A a;
h(a);
j(a);
}