I was wondering why below code does not compile with gcc (4.6.3) while it works perfectly fine using cl. I can obviously compile it with gcc by removing the template argument of the DoSomething function. Nonetheless I'd like to understand why the syntax below is not correct.
Thanks for any hints.
template <class T>
class Base {
public:
template <class B>
struct Var1 {
B var1_var;
};
Base() {};
~Base() {};
template <class V> void DoSomething(V val) {
std::cout << val.var1_var << std::endl;
}
};
template <class T>
class Derived : public Base<T> {
public:
Derived() {};
~Derived() {};
void Test() {
typename Base<T>::template Var1<int> val;
val.var1_var = 5;
Base<T>::DoSomething<typename Base<T>::template Var1<int> >(val); //error: expected ‘(’ before ‘>’ token
}
};
int main(int, char**) {
Derived<double> bla;
bla.Test();
return 0;
}
The problem is that Test is a nondependant name (it does not involve T) so the compiler won't look in dependant base class while looking for the function Dosomething.
You can solve this problem like you did (with 0x499602D2 's answer), but it will not allow virtual dispatch mechanism. You can solve this just by using this->DoSomething(val) also, that will enable virtual dispatch.
More information here.
You need another template:
Base<T>::template DoSomething<typename Base<T>::template Var1<int> >(val);
// ^^^^^^^^
Update: You also don't need to provide the template parameter. You can allow template argument deduction to do it for you:
DoSomething(val);
Related
I have a template base class with a template parameter of type bool. The constructor parameter list of this base class depends on whether the template parameter is true or false. I want to derive from this class another template class with a template parameter that is any type. I need this derived class to call the correct constructor of that Base class depending on traits of that type.
The example below is not all-encompassing. Integral or not, the base class template bool could be for any type trait. Also, the type passed to the template parameter of the derived class could be any type.
I've tried several different methods for trying to syntax this thing out, but the closest I've gotten is with the code below. However it errors due to the fact that the derived class needs to be fully specialized.
#include <type_traits>
using namespace std;
template<bool IsIntegral> struct Base
{
template<bool IsI = IsIntegral,
typename I = typename enable_if<IsI>::type>
Base(int param) {}
template<bool IsI = IsIntegral,
typename I = typename enable_if<!IsI>::type>
Base() {}
};
template<class T> class CL :
Base<is_integral<T>::value>
{
public:
template<bool U = is_integral<T>::value> CL();
};
template<>
template<class T>
CL<T>::CL<true>() : // error: ISO C++ forbids declaration of ‘CL’ with no type [-fpermissive]
// CL<T>::CL<true>() :
// ^
// error: non-type partial specialization ‘CL<true>’ is not allowed
// error: no declaration matches ‘int CL<T>::CL()’
// CL<T>::CL<true>() :
// ^~~~~
Base(4) { }
template<>
template<class T>
CL<T>::CL<false>() // error: ISO C++ forbids declaration of ‘CL’ with no type [-fpermissive]
// CL<T>::CL<true>() :
// ^
// error: non-type partial specialization ‘CL<true>’ is not allowed
// error: no declaration matches ‘int CL<T>::CL()’
// CL<T>::CL<true>() :
// ^~~~~
{ }
int main () {
// Base<true> a; // won't compile as expected
Base<true> a(4);
// Base<false> b(4); // won't compile as expected
Base<false> b;
CL<int> c; // integral
CL<int*> d; // non-integral
// should work for any types other than int and int*
return 0;
}
I need to keep the type T generic and can't fully specialize the class CL. What is the correct syntax for this? Is there a workaround?
I'm using gcc-g++ version 8.3.0-6.
Thanks in advance!
After playing around with this, a little bit, I came up with this, which I guess falls under the category of "workaround", which you are willing to consider:
template<class T> class CL :
Base<is_integral<T>::value>
{
public:
CL() : CL{ is_integral<T>{} } {}
template<typename U=T>
CL(std::true_type) : Base<true>{4} {}
template<typename U=T>
CL(std::false_type)
{
}
};
Your derived class should be:
template<class T> class CL :
Base<is_integral<T>::value>{
public:
using base_t = Base<is_integral<T>::value>;
using base_t::base_t;
};
The using base_t::base_t; makes you inherit the constructors from your base class (available since c++11).
In your example you should also change:
CL<int> c; // integral
into, e.g.:
CL<int> c(10); // integral
C++20 and requires clauses
Prior to C++20, I would recommend tag dispatch to private delegating constructors as shown in #SamVarshavchik's answer.
Once you may use C++20, you can easily construct these kind of relations using a requires clause:
#include <type_traits>
template <bool IsIntegral>
struct Base {
Base(int param) requires IsIntegral {}
Base() requires (!IsIntegral) {}
};
template<typename T>
class CL : Base<std::is_integral<T>::value> {
static constexpr bool kIsIntegral = std::is_integral_v<T>;
public:
CL() requires kIsIntegral : CL::Base(4) {}
CL() requires (!kIsIntegral) : CL::Base() {}
};
Why the *this of the derived<T> class is still of base<T> type?
I thought the typename X would take care of that.
If this looks ugly, what is a better way?
Failed Attempt:
template <typename T>
class Derived;
template <typename T, typename X>
class Base{
public:
T val;
X f(void){
return *this;
}
};
template <typename T>
class Derived: public Base<T, Derived<T> >{
};
int main(void){
Derived<int> B;
Derived<int> C = B.f();
}
Error:
test4.cpp(9): error: no suitable user-defined conversion from "Base<int, Derived<int>>" to "Derived<int>" exists
return *this;
^
detected during instantiation of "X Base<T, X>::f() [with T=int, X=Derived<int>]" at line 20
compilation aborted for test4.cpp (code 2)
You may do the downcast with:
X f(){ return static_cast<X&>(*this);}
When the compiler looks at class Base<int, Derived<int>> it has no reason to believe that there is actually a Derived<int> that inherited from it. One could do class Other : public Base<T, Derived<T>>{} and the conversion from class Base<int, Derived<int>> to Derived<int> would be incorrect, so an automatic conversion is not allowed.
If you make a rule that says that the second template parameter must be the derived type that inherits that base (which class Other violates) you can bypass the type system with a cast, but make sure the rule is not violated.
You have
X f(void){
return *this;
}
In this function, type of *this is still the base class type. There is no automatic conversion from the base type to X.
I am unable to suggest a clean solution since X can be anything at that point, not necessarily Derived<T>.
I can use:
template <typename T>
class Derived2 : public Base<T, double>
{
};
How's the base class supposed to deal with that?
The approach used in f() seems to be a design flaw.
Update
If Derived is guaranteed to have the form
template <typename T>
class Derived : public Base<T, Derived<T>>
{
};
Then, you can use:
X& f(void){
return static_cast<X&>(*this);
}
Please note that I changed the return type from X to X&. It avoids the cost of making a copy every time you call the function.
First of all, I would modify Base so that it accepts only one template parameter.
This way, it matches exactly the common form shown for the CRTP idiom.
Actually, it seems to me that there is no reason to use both T and Derived<T> as a template parameter in this case, for the latter already contains the former and T, Derived<T> is the pattern to which you want to adhere:
template<typename>
class Base;
template <typename T, template<typename> typename X>
class Base<X<T>> {
// ...
};
template <typename T>
class Derived: public Base<Derived<T>>{
// ....
};
Then you can use a promotion from the bottom as mentioned by #Jarod42:
X<T> f(void) {
return *static_cast<X<T>*>(this);
}
Note that here you are making systematically a copy of the object and probably it is not what you want.
Another option is to modify a bit the architecture and use a covariant return type (it follows a minimal, working example):
template <typename T>
class Base {
T val;
public:
virtual const Base<T>& f(void) const {
return *this;
}
};
template <typename T>
class Derived: public Base<T> {
public:
virtual const Derived<T>& f(void) const {
return *this;
}
};
int main(void) {
Derived<int> B;
Derived<int> C = B.f();
}
Which one is better for you mostly depends on the actual problem.
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.
I'm getting an error when compiling a code that can be trivialized as follows:
#include<iostream>
template <class T>
class A
{
protected:
T protectedValue;
template<class TT>
class insideClass
{
public:
TT insideClassValue;
};
};
template<class T>
class B : public A<T>
{
public:
void print(T t)
{
insideClass<T> ic; // <-- the problem should be here
ic.insideClassValue = t;
std::cout << ic.indideClassValue << std::endl;
};
};
int main()
{
double v = 2.;
B<double> b;
b.print(v);
return 0;
};
The compiler (g++) gives the following error:
main.C: In member function ‘void B<T>::printA()’:
main.C:23:4: error: ‘insideClass’ was not declared in this scope
main.C:23:17: error: expected primary-expression before ‘>’ token
main.C:23:19: error: ‘ic’ was not declared in this scope
I figured out that if class A is not a template class, the compilation will not give any error.
I do not understand why making class A a template class results in the described error.
Any idea about the reasons and how to fix the problem?
Without qualification insideClass is a non-dependent name which is looked up during phase 1 look-up. Since the definition of the base depending on a template argument isn't known, names from the base class are ignored and the name isn't found. Qualification and possibly adding typename in a strategic place should solve the problem (thanks to remyabel for the notation):
typename A<T>::template insideClass<T> ic;
The template keyword is needed to indicate that what's coming is a template and the typename is needed to indicate that happens to be a type. Getting the correct spelling of what a dependent name is supposed to be is sometimes not entirely straight forward. A SSCCE showing the problem is here and the solution is here.
Something like this:
typedef typename A<T>::template insideClass<T> ic;
public:
void print(T t)
{
ic ic;
ic.insideClassValue = t;
std::cout << ic.insideClassValue << std::endl;
};
In all the languages that I understand this is not possible but someone was telling me it was possible in C++ but I have a hard time believing it. Essentially when you parameterize a class you are creating a unique class in the compilation stage aren't you?
Let me know if I am not being clear with my question.
Here is my attempt at explaning what I am trying to do ( pay attention to class L ):
//; g++ ModifingBaseClassParameter.cpp -o ModifingBaseClassParameter;ModifingBaseClassParameter
#include <iostream>
using namespace std;
template<typename T>
class Base
{
public:
Base() {}
Base(T& t) : m_t(t) {}
T& getMem() {return m_t;}
private:
T m_t;
};
template<typename T>
class F: Base<T>
{};
template<typename T>
class L: F<long>
{};
int main()
{
Base<int> i;
F<float> f;
L<long> l;
cout<<i.getMem()<<endl;
// cout<<f.getMem()<<endl; // why doesn't this work
// cout<<l.getMem()<<endl; // why doesn't this work
}
So as you can see (hopefully my syntax makes sense) class L is trying to redefine its parent's float parameter to be a long. It certainly doesn't seem like this is legal but I will differ to the experts.
If you mean to ask whether you can do this in c++ :
template <>
class ParamClass<Type1> : public ParamClass<Type2>
{
};
then yes, it is possible.
It is very often used, for example to define template lists or inherit traits from another type.
What you've asked for can't be done directly -- but you can come pretty close by using a default template parameter:
template <typename T>
class Base { };
template <typename T = int>
class X : Base<T> {};
class Y : Base<float>
class Z : X<long> {};
In this particular case, the default template parameter doesn't add much. You have to supply a template parameter list to instantiate a template, even if defaults are provided for all the parameters. As such, having a default that you override in a derived class is usually only useful for the second and subsequent parameters.
#include <iostream>
#include <typeinfo>
using namespace std;
template<typename T>
class Base
{
public:
Base() {}
Base(T& t) : m_t(t) {}
T& getMem() {return m_t;}
private:
T m_t;
};
template<typename T>
class F: public Base<T>
{};
template<typename T>
class L: public F<long>
{};
int main()
{
Base<int> iNTEGER;
F<float> fLOAT;
L<long> lONG;
int x;
cout << typeid(iNTEGER.getMem()).name() << endl;
cout << typeid(fLOAT.getMem()).name() <<endl; // this works now :)
cout << typeid(lONG.getMem()).name() <<endl; // this works now :)
}
the inheritance is private by default in c++, once made public what stephenmm wrote should work unless you meant to ask something else ?
Are you taking about templates?
template<typename T>
class Base
{
public:
Base() {}
Base(T& t) : m_t(t) {}
T& getMem() {return m_t;}
private:
T m_t;
};
class X: Base<int>
{};
class Y: Base<float>
{};
You should try compiling it:
$ g++ so-test1.c++ -o so-test1.c++ && ./so-test
so-test1.c++:21: error: expected template-name before ‘<’ token
so-test1.c++:21: error: expected `{' before ‘<’ token
so-test1.c++:21: error: expected unqualified-id before ‘<’ token
so-test1.c++: In function ‘int main(int, const char**)’:
so-test1.c++:27: error: aggregate ‘Z z’ has incomplete type and cannot be defined
X is not a class template, so it makes no sense to try to instantiate it in
class Z: X<long> {};
X has no template parameters to override. Remember that
Base<int>
is not a class template either; it is a class in its own right, but fully instantiated from a template. You could do this:
....
template<typename T>
class X: Base<T>
{};
...
class Z: X<long>
{};
But here there is no confusion about overriding any template parameters.
template<typename T>
class X: Base<int>
{};
...
class Z: X<long>
{};
works too, but here the template parameter in X is unused, and nothing is overridden.
HTH