I'm trying to understand whay i get an error on this code:
(the error is under g++ unix compiler. VS is compiling OK)
template<class T> class A {
public:
T t;
public:
A(const T& t1) : t(t1) {}
virtual void Print() const { cout<<*this<<endl;}
friend ostream& operator<<(ostream& out, const A<T>& a) {
out<<"I'm "<<typeid(a).name()<<endl;
out<<"I hold "<<typeid(a.t).name()<<endl;
out<<"The inner value is: "<<a.t<<endl;
return out;
}
};
template<class T> class B : public A<T> {
public:
B(const T& t1) : A<T>(t1) {}
const T& get() const { return t; }
};
int main() {
A<int> a(9);
a.Print();
B<A<int> > b(a);
b.Print();
(b.get()).Print();
return 0;
}
This code is giving the following error:
main.cpp: In member function 'const T& B::get() const':
main.cpp:23: error: 't' was not declared in this scope
It did compiled when i changed the code of B to this:
template<class T> class B : public A<T> {
public:
B(const T& t1) : A<T>(t1) {}
const T& get() const { return A<T>::t; }
};
I just cant understand what is the problem with the first code...
It doesn't make sense that i really need to write "A::" every time...
You can also use this->t to access the base class template member.
In B::get(), the name t is not dependent on the template parameter T, so it is not a dependent name. Base class A<T> is obviously dependent on the template parameter T and is thus a dependent base class. Nondependent names are not looked up in dependent base classes. A detailed description of why this is the case can be found in the C++ FAQ Lite.
Related
I am trying to write a hash function for my custom class, and make my code compatible with both gcc 11.1 and clang 12.0.0, but clang gives the warning/error:
<source>:30:25: warning: dependent nested name specifier 'std::hash<Base<U>>::' for friend class declaration is not supported; turning off access control for 'Base' [-Wunsupported-friend]
std::hash<Base<U>>::operator()(const Base<U>& b) const noexcept;
<source>:38:26: error: 'data_' is a private member of 'ns::Base<int>'
std::size_t seed = b.data_.size();
for the code below (also at https://godbolt.org/z/8Wsnqr5cb). How can I fix it to be compatible with both?
#include <set>
#include <utility>
#include <iostream>
// Forward declaration
namespace ns
{
template<typename T>
class Base;
}
/// Hash specialization declaration
template<typename T>
struct std::hash<ns::Base<T>>
{
std::size_t operator()(const ns::Base<T>& b) const noexcept;
};
namespace ns
{
template<typename T>
class Base {
std::set<T> data_;
public:
Base(const std::set<T>& data): data_{data} {}
template<typename U>
friend std::size_t
std::hash<Base<U>>::operator()(const Base<U>& b) const noexcept;
};
}
// In implementation file:
template<typename T>
std::size_t std::hash<ns::Base<T>>::operator()(const ns::Base<T>& b) const noexcept
{
std::size_t seed = b.data_.size();
for(const auto & x : b.data_)
seed ^= x;
return seed;
}
using namespace ns;
int main()
{
std::set<int> data({1,2,3,4,5});
Base<int> a(data);
std::cout << std::hash<Base<int>>{}(a) << std::endl;
}
You can change the friend declaration only valid for the Base of current template parameter T, i.e. current instantiation.
template<typename T>
class Base {
std::set<int> data_;
public:
Base(const std::set<int>& data): data_{data} {}
friend std::size_t
std::hash<Base<T>>::operator()(const Base<T>& b) const noexcept;
//or just
//friend std::size_t
//std::hash<Base>::operator()(const Base& b) const noexcept;
};
Note that the effect is different with yours. With the above friend declaration, for example, given Base<int>, only std::hash<Base<int>>::operator() is the friend. With template friend declaration in your code, given Base<int>, all the possible instantiations like std::hash<Base<int>>::operator(), std::hash<Base<char>>::operator(), ... are friends.
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
I have a set of classes implementing the curiously recurring template pattern. However, the trick is that the base class needs to return instances of the subclasses. Here's an example:
template <typename SubType>
class ArithmeticBase
{
public:
template <typename OtherType>
const Addition operator+(const OtherType &other)
{return Addition(get_subclass(), other);}
// ...
// Operators for subtraction, multiplication, division, ...
private:
const SubType &get_subclass() const
{return *static_cast<const SubType*>(this);}
};
template <typename OperatorType1, typename OperatorType2>
class Addition : ArithmeticBase<Addition<OperatorType1, OperatorType2>>
{
public:
Addition(const OperatorType1 &op1, const OperatorType2 &op2)
: op1(op1)
, op2(op2)
{}
private:
const OperatorType1 &op1;
const OperatorType2 &op2;
};
// ...
// Additional classes for subtraction, multiplication, division, ...
Compiling this fails because the Addition class is not defined before it's used in the ArithmeticBase class:
arithmetic.cpp:6:8: error: unknown type name 'Addition'
const Addition operator+(const OtherType &other)
^
How can I resolve this?
You could forward declare Addition before the base class.
template <typename OperatorType1, typename OperatorType2>
class Addition;
template <typename SubType>
class ArithmeticBase
{
...
};
This allows the compiler to know there is a type Addition that exists before it is defined.
Or use non-member form declared after Addition:
template <typename OperatorType1, typename OperatorType2>
class Addition;
template <typename SubType>
class ArithmeticBase
{
public:
template <typename OneType, typename OtherType>
friend const Addition<OneType, OtherType> operator+(const ArithmeticBase<OneType>& one, const OtherType &other);
private:
const SubType &get_subclass() const
{
return *static_cast<const SubType*>(this);
}
};
class ArithmeticType : public ArithmeticBase < ArithmeticType > {};
template <typename OperatorType1, typename OperatorType2>
class Addition : ArithmeticBase<Addition<OperatorType1, OperatorType2>>
{
public:
Addition(const OperatorType1 &op1, const OperatorType2 &op2)
: op1(op1)
, op2(op2)
{}
private:
const OperatorType1 &op1;
const OperatorType2 &op2;
};
template <typename OneType, typename OtherType>
const Addition<OneType, OtherType> operator+(const ArithmeticBase<OneType>& one, const OtherType &other)
{
return Addition<OneType, OtherType>(one.get_subclass(), other);
}
int main()
{
ArithmeticType a, b;
a + b;
}
In addition to forward declaring the Addition class (as bhzag's answer shows) you'll need to move the definition of operator+ to after the definition the Addition class. Otherwise you'll get an error on the next line.
Make sure the definition is in the header file. If it isn't, you'll get linker errors.
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;
^