I have some trouble getting this to work. Here is a MVCE of my problem that passes the compilation phase
template<typename T>
struct foo
{
using type = T;
friend type bar(foo const& x) { return x.X; }
foo(type x) : X(x) {}
private:
type X;
};
template<typename> struct fun;
template<typename T> fun<T> bar(foo<T> const&, T); // forward declaration
template<typename T>
struct fun
{
using type = T;
friend fun bar(foo<type> const& x, type y)
{ return {bar(x)+y}; }
private:
fun(type x) : X(x) {}
type X;
};
int main()
{
foo<int> x{42};
fun<int> y = bar(x,7); // called here
};
The forward declaration is required for the compiler to resolve the call in main() (see this answer for the reason). However, the compiler now complains in the linking/loading phase:
Undefined symbols for architecture x86_64: "fun
bar(foo const&, int)", referenced from:
_main in foo-00bf19.o ld: symbol(s) not found for architecture x86_64
even though the function is defined in the friend declaration. If instead, I move the definition outside of that of struct func<>, i.e.
template<typename T>
struct fun
{
using type = T;
friend fun bar(foo<type> const& x, type y);
private:
fun(type x) : X(x) {}
type X;
};
template<typename T>
inline fun<T> bar(foo<T> const& x, T y)
{ return {bar(x)+y}; }
compilation fails with
foo.cc:29:10: error: calling a private constructor of class 'fun<int>'
{ return {bar(x)+y}; }
So, how can I get this to work? (compiler: Apple LLVM version 9.0.0 (clang-900.0.39.2), c++11)
Friend declaration inside of fun must match function template forward declaration, otherwise it will spawn an unrelated function:
template<typename TT> fun<TT> friend ::bar(foo<TT> const &, TT);
While definition should be placed outside:
template<typename T> fun<T> bar(foo<T> const& x, T y)
{ return {bar(x)+y}; }
online compiler
The shorter code to demonstrate the problem would be:
void foo(void);
template<typename T>
struct bar
{
friend void foo(void) {}
};
int main()
{
foo(); // undefined reference to `foo()'
return 0;
}
In-class function definition will never be used:
17.8.1 Implicit instantiation [temp.inst]
The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions, member classes, scoped member enumerations, static data members, member templates, and friends;
There are some quirky rules around friend functions.
namespace X {
template<class T>
struct A{
friend void foo(A<T>) {}
};
}
foo above is not a template function. It is a non-template friend function which exists in the namespace enclosing A but it can only be found via ADL lookup; it cannot be directly named as X::foo.
Much like members of templates can be templates themselves, or not, this is a non-template friend function that is created for each template class instantiation of A.
namespace X{
template<class T>
void foo(A<T>);
}
this foo is a function template named foo in namespace X. It is not the same as the non-template friend function foo above.
From this, most of your errors are clear. What you thought was a forward declaration was an unrelated template function. What you thought was a friend, was not, so you didn't have access to the private constructor.
We can fix this a few ways. My favorite way is to add a tag type.
template<class T>struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag{};
now we can use tag to ADL dispatch:
template<typename T>
struct fun {
using type = T;
friend fun bar(tag_t<fun>, foo<type> const& x, type y)
{ return {bar(x)+y}; }
private:
fun(type x) : X(x) {}
type X;
};
and then in main this works:
foo<int> x{42};
fun<int> y = bar(tag<fun<int>>, x, 7); // called here
But you might not want to mention tag<fun<int>>, so we just create a non-friend bar that does the call for us:
template<class T>
fun<T> bar(foo<T> const& x, type y)
{ return bar( tag<T>, x, y ); }
and now ADL does its magic and the proper non-template bar is found.
The other approach involves making the template function bar be a friend of fun.
Okay, I found an answer:
In order for the friend declaration to work properly, it must be qualified as a function template, i.e.
template<typename T>
struct fun
{
using type = T;
friend fun bar<T>(foo<type> const& x, type y);
// ^^^
private:
fun(type x) : X(x) {}
type X;
};
template<typename T>
inline fun<T> bar(foo<T> const& x, T y)
{ return {bar(x)+y}; }
However, combining the definition with the friend declaration still fails:
template<typename T>
struct fun
{
using type = T;
friend fun bar<T>(foo<type> const& x, type y)
{ return {bar(x)+y}; }
private:
fun(type x) : X(x) {}
type X;
};
results in:
foo.cc:20:16: warning: inline function 'bar<int>' is not defined [-Wundefined-inline]
friend fun bar<T>(foo<T> const& x, T y)
^
1 warning generated.
Undefined symbols for architecture x86_64:
"fun<int> bar<int>(foo<int> const&, int)", referenced from:
_main in foo-c4f1dd.o
ld: symbol(s) not found for architecture x86_64
which I don't really understand as the forward declaration is still in place.
This one compiles for me:
template<typename T>
struct foo
{
using type = T;
friend type bar(foo const& x) { return x.X; }
foo(type x) : X(x) {}
private:
type X;
};
template<typename> struct fun;
template<typename T> fun<T> bar(foo<T> const&, T); // forward declaration
template<typename T>
struct fun
{
using type = T;
friend fun bar<type>(foo<type> const& x, type y);
private:
fun(type x) : X(x) {}
type X;
};
template<typename T>
inline fun<T> bar(foo<T> const& x, T y)
{ return {bar(x)+y}; }
int main()
{
foo<int> x{42};
fun<int> y = bar(x,7); // called here
};
Using GCC 8.2.1. What I added is the template designation to the friend declaration.
Related
Is it possible to make this code work as I'd like? I.e. to allow the concept to have access to a private member funcion?
template <typename T>
concept bool Writeable()
{ return requires (T x,std::ostream os) { { x.Write(os) } -> void }; }
template <Writeable T>
void Write(std::ostream &os,const T &x) { x.Write(os); }
class TT
{
private:
void Write(std::ostream &os) const { os << "foo"; }
//friend concept bool Writeable<TT>();
friend void ::Write<TT>(std::ostream &,const TT &);
};
Thanks
No. Concepts explicitly are not allowed to be friends.
n4377 7.1.7/2
Every concept definition is implicitly defined to be a constexpr
declaration (7.1.5). A concept definition shall not be declared with
the thread_local, inline, friend, or constexpr specifiers, nor shall a
concept definition have associated constraints (14.10.2).
We can reduce it to this example to show that the access really is the problem:
template <typename T>
concept bool Fooable = requires (T t) { { t.f() } -> void };
struct Foo
{
private:
void f() {}
};
int main()
{
static_assert(Fooable<Foo>, "Fails if private");
}
You can however use a level of indirection, something like this:
template <typename T>
void bar(T t) { t.f(); }
template <typename T>
concept bool FooableFriend = requires(T t) { { bar(t) } -> void };
struct Foo
{
private:
void f() {}
template<typename T>
friend void bar(T t);
};
int main()
{
static_assert(FooableFriend<Foo>, "");
}
Live demo incorporating your example
Which works. Concepts are pretty early, so I imagine down the line that they might lift the friend restriction just as proposals have lifted restrictions for C++11/14 features in the past.
For the following code, I got a compilation error at implementation line as:
"B does not define a type".
I am aware of solution of put the function definition inside of class declaration. Is it possible, though, to have the function definition out of the template class declaration? Thanks
template<typename T>
class A {
public:
// ctor, dtor and interface funcs etc
private:
struct B {
T value;
B *next;
}
B *locate(const T& val) const;
// blah blah
};
template<typename T>
B *A<T>::locate(const T& val) const
{
//logic
}
Since B is defined inside A you should qualify it with A<T>:::
template<typename T>
typename A<T>::B *A<T>::locate(const T& val) const
{
//logic
}
Also note typename which is required because B is a dependent name.
In function myfun is there a way to access rhs.var without writing a public function which returns var? Also, as I understand, this happens because rhs could be a different type... Is this correct?
#include <iostream>
template<class T>
class foo
{
private:
T var;
public:
foo(T v) : var(v) {}
template<class Type>
void myfun(foo<Type>& rhs)
{
auto i = rhs.var; //BOOM
}
};
int main()
{
foo<int> a = 5;
foo<double> b = 2.2;
a.myfun(b);
}
Suggested Solutions
You could either provide a public accessor to your private member variable:
template<class T>
class foo {
T var;
public:
foo(T v) : var(v) {}
T getVar() const { return var; }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
template<class Type>
void myfun(foo<Type>& rhs) {
auto i = rhs.getVar();
^^^^^^^^
}
};
Or as already Dieter mentioned in the comments you could make your template class a friend:
template<class T>
class foo {
T var;
template <class> friend class foo;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
public:
foo(T v) : var(v) {}
template<class Type>
void myfun(foo<Type>& rhs) {
auto i = rhs.var;
}
};
Overview
The reason why the template member function myfun is not granted access to private member variable var of class template foo is that the compiler interprets class foo<Type> and class foo<T> as completely different class types, even though they would originate from the same template class definition. Thus, as being different class types the one cannot access the private members of the other.
you can define the second type as fried like:
template<class T>
class foo
{
private:
T var;
public:
foo(T v) : var(v) {}
template<class Type>
void myfun(foo<Type>& rhs)
{
auto i = rhs.var; //BOOM
}
template<class Type>
friend class foo;
};
live example
Note: This question is really close to Return type deduction for in-class friend functions, but I did not find the answer to my problem there.
Tested with clang 3.4 with std=c++1y and clang 3.5 with std=c++14 and std=c++1z
This code compiles:
#include <iostream>
template<class T>
class MyClass {
public:
MyClass(T const& a) : impl(a) {}
template<class T0, class T1> friend auto
// requires operator+(T0,T1) exists
operator+(MyClass<T0> const& a, MyClass<T1> const& b)
{
return MyClass<decltype(a.impl+b.impl)>{a.impl + b.impl};
}
T getImpl() const { return impl; }
private:
T impl;
};
int main() {
MyClass<int> x(2);
MyClass<long> y(2);
auto z = x+y;
std::cout << z.getImpl() << "\n";
}
Now if I define operator+ outside of the class, it does not compile anymore:
template<class T>
class MyClass {
public:
MyClass(T const& a) : impl(a) {}
template<class T0, class T1> friend auto
operator+(MyClass<T0> const& a, MyClass<T1> const& b);
T getImpl() const { return impl; }
private:
T impl;
};
template<class T0, class T1> auto
operator+(MyClass<T0> const& a, MyClass<T1> const& b)
{
return MyClass<decltype(a.impl+b.impl)>{a.impl + b.impl};
}
Clang 3.4 says:
error: use of overloaded operator '+' is ambiguous (with operand types MyClass<int> and MyClass<long>)
And then points at what it believes to be two different functions: the declaration in the class and the definition outside the class.
My question is: is it a clang bug, or just that template parameters are deduced for a friend function thus leading the two functions not being equivalent is some cases ?
And what alternative would you suggest: make operator+ a member function, or define friend operator+ inside the class (which would in my opinion clutter the class interface) ?
Just for your information, I have a real use case of such code, where I try to wrap a third -party matrix class and I need return type deduction because of the use of expression template for lazy evaluation.
Edit: The following does work (but still clutters the interface...)
template<typename T>
class MyClass
{
T impl;
public:
explicit MyClass(T a) : impl(std::move(a)) { }
T const& getImpl() const { return impl; }
template<typename T0, typename T1>
friend auto operator +(MyClass<T0> const& a, MyClass<T1> const& b) -> MyClass<decltype(a.impl + b.impl)>;
};
template<typename T0, typename T1>
auto operator +(MyClass<T0> const& a, MyClass<T1> const& b) -> MyClass<decltype(a.impl + b.impl)>
{
return MyClass<decltype(a.impl + b.impl)>(a.impl + b.impl);
}
edit:
look at the comments section, it's a bug in gcc 4.8.2 and 4.9
Gcc error code:
prog.cpp:10:61: error: non-static data member declared 'auto'
operator+(MyClass const& a, MyClass const& b)
^ prog.cpp: In function 'int main()': prog.cpp:25:15: error: no match
for 'operator+' (operand types are 'MyClass' and 'MyClass')
auto z = x+y;
^
Following and example I took from another question, I understand why this does not work:
struct Foo {
Foo() {}
Foo(int) {}
Foo operator+(Foo const & R) { return Foo(); }
};
struct Bar {
Bar() {}
Bar(int) {}
};
Bar operator+(Bar const & L, Bar const & R) {
return Bar();
}
int main() {
Foo f;
f+1; // Will work - the int converts to Foo
1+f; // Won't work - no matching operator
Bar b;
b+1; // Will work - the int converts to Bar
1+b; // Will work, the int converts to a Bar for use in operator+
}
But then, if I change it to use templates in this way:
template <class T>
struct Foo {
Foo() {}
Foo(T) {}
Foo operator+(Foo const & R) { return Foo(); }
};
template <class T>
struct Bar {
Bar() {}
Bar(T) {}
};
template <class T>
Bar operator+(Bar const & L, Bar const & R) {
return Bar();
}
int main() {
Foo<int> f;
f+1; // Will work - the int converts to Foo
1+f; // Won't work - no matching operator
Bar<int> b;
b+1; // DOES NOT WORK
1+b; // DOES NOT WORK
}
It does not work. Can anyone put some light on this? Templates are driving me crazy.
Thanks.
There are two problems.
You need to add the template type to the arguments in the definition of the operator. This is necessary because it needs to use them to know which instantiation of Bar to use.
If you want mixed operators (that operate on two separate types) in a template function, you're going to need to provide the definitions for all of the mixed cases. Otherwise, the template deduction system won't work the way you want.
.
template <class T>
Bar<T> operator+(Bar<T> const & L, Bar<T> const & R) { // Fixed
return Bar<T>();
}
template <class T>
Bar<T> operator+(Bar<T> const & L, const T & R) { // Added
return L + Bar<T>(R);
}
template <class T>
Bar<T> operator+(const T& L, Bar<T> const & R) { // Added
return Bar<T>(L) + R;
}
There's no implicit conversion between int and Bar, only between int and Bar<int>. In fact, you haven't defined a type Bar; instead, you have a family of types Bar<T> for various values of T. Probably you're seeing a compiler error connected to the definition of operator+, though it's likely hidden behind a thicket of other templated-type errors.
I don't have a c++ compiler in front of me, but I believe template<class T> Bar<T> operator+(Bar<T> const & L, Bar<T> const & R) will behave as you expect.