Let's say we have a base class that does nothing but contain other values and performs certain operations on them, such as printing them:
template <typename T>
class Base {
public:
Base (const T& t) : value(t) {}
void printValue () {
cout << value << endl;
}
protected:
T value;
};
Then let's say we want to derive from this template class to provide additional functionality. For the sake of this discussion, I'll keep it simple and just say we're adding the ability to change the contained object's value. Setting value = t will not work in derived template functions, but qualifying the value with this-> will:
template <typename T>
class Derived: public Base<T> {
public:
Derived (const T& t) : Base<T>(t) {}
void setValue (const T& t) {
this->value = t;
}
};
But now, let's say I want to specialize a function for certain types of instantiated objects. Say, if a type can be incremented, like int, I want to provide that functionality:
template <>
class Derived<int> {
public:
void increment () {
this->value++;
}
};
This does not work, because the compiler will not recognize the base class' property value. I've also tried inserting a using Base<int>::value declaration before the increment, and it still doesn't work (at least, not with g++). I get a compiler error ‘Base<int>’ is not a namespace
Is there a way to refer to base template class' properties from a specialized template function? Using this-> works for derived template classes, but not for specialized functions for derived classes. I've also found that puting using Base<T>::value in Derived::setValue rather than using this-> doesn't work, although I thought it should. Any thoughts?
Update: This is the complete code:
template <typename T>
class Base {
public:
Base (const T& t) : value(t) {}
void printValue () {
cout << value << endl;
}
protected:
T value;
};
template <typename T>
class Derived: public Base<T> {
public:
Derived (const T& t) : Base<T>(t) {}
void setValue (const T& t) {
this->value = t;
}
};
template <>
class Derived<int>: public Base<int> {
public:
void increment () {
//using Base<int>::value;
this->value++;
}
};
And these are the errors:
templatebase.cpp: In function ‘int main()’:
templatebase.cpp:36:21: error: no matching function for call to ‘Derived<int>::Derived(int)’
templatebase.cpp:36:21: note: candidates are:
templatebase.cpp:27:7: note: constexpr Derived<int>::Derived(const Derived<int>&)
templatebase.cpp:27:7: note: no known conversion for argument 1 from ‘int’ to ‘const Derived<int>&’
templatebase.cpp:27:7: note: constexpr Derived<int>::Derived(Derived<int>&&)
templatebase.cpp:27:7: note: no known conversion for argument 1 from ‘int’ to ‘Derived<int>&&’
Related
With a class defined as follows:
template <typename T>
class A {
private:
T a;
public:
A(T& a) : a_(a) { }
template <typename D>
void Eval(D& arg)
{
// ...
}
};
template A<int>;
I want to explicitly instantiate one instance of the class, and I want this class to have one explicit instantiation of Eval. The intention here is to get a member function pointer that avoids ambiguity:
auto eval_ptr = &A<int>::Eval;
The ambiguity is not coming from anything to do with template instantiation of the class, it's caused by Eval also being a templated function.
&A<int>::Eval does not point to a function, it points to a template. And there is just no such type as a "pointer to a template".
If you want a pointer to A<int>::Eval, you need to specify D as well.
auto eval_ptr = &A<int>::Eval<int>; works just fine for example.
Addendum: Pointers-to-templates do exist in the grammatical sense, but there is no type an object can have to hold one of them. They must be immediately casted/decayed to a specific overload in order to be used, which doesn't come into play here since you want to store it in an auto.
For example: The following is fine because there's clearly only one "version" of Eval that can be meant:
void bar(void (A<int>::*arg)(int&)) {}
void foo() {
bar(&A<int>::Eval);
}
The very simple solution was specifying both template parameters:
template <typename T>
class A
{
private:
T a;
public:
A(T &a) : a_(a) {}
template <typename D>
void Eval(D &arg)
{
arg+=1;
}
};
int main()
{
auto p = &A<int>::Eval<int>;
}
I want to use "static polymorphism" via CRTP to do something like the following:
template <typename T>
struct Base
{
double get_value() const { return ((T*)this)->_get_value(); }
protected:
~Base() {}
};
struct Derived1 : public Base<Derived1>
{
double value;
Derived1() : value(3.) {}
const double& _get_value() const { return value; }
};
struct Derived2 : public Base<Derived2>
{
double _get_value() const { return 5.; }
};
This works, but I'd also like that for the case of the object being instantiated as Derived1, get_value returns a const reference to the value instead of returning a copy. So in a way, a kind of "perfect forwarding" for return values.
I tried to declare get_value's return type like this:
template <typename T>
struct Base
{
decltype(std::declval<T>()._get_value()) get_value() const { return ((T*)this)->_get_value(); }
...
but unsurprisingly GCC complained that this is an invalid use of incomplete type 'struct Derived1'.
Is there any way to go around this?
Thank you in advance! :)
The reason why GCC is rejecting the proposed solution in the OP is that Base<Derived1> is being instantiated before Derived1. This instantiation consists of an instantiation of the signatures of all member functions, but it doesn't instantiate the function bodies themselves.
So we need to defer determination of the signature/return type of the member function until after Derived1 is visible.
One way to do this is by deferring determination of the return type via decltype(auto). This makes the return type dependent on the function body (which is not immediately instantiated). It's a C++14 feature, unfortunately.
template <typename T>
struct Base
{
decltype(auto) get_value() const { return ((T*)this)->_get_value(); }
protected:
~Base() {}
};
See https://godbolt.org/z/r1T56n
This is probably covered by dcl.spec.auto and temp.inst.
Alternatively, even in C++11, you can postpone determination of the return type by turning the function into a function template, dependent on some dummy parameter if necessary:
template <typename T>
struct Base
{
template<typename U=T>
decltype(std::declval<U>()._get_value()) get_value() const {
return ((T*)this)->_get_value();
}
protected:
~Base() {}
};
I can't seem to call a method of a base class without scoping to the base class, and it seems that this is because I have overloaded the method. If I do not overload the method then the compiler doesn't complain. Here's an example of what I'd like to do:
struct BaseClass {
template <typename T> T& foo(T& t) {
return t;
}
};
class ChildClass: public BaseClass {
public:
// The presence of this template causes compiler confusion
template <class T> T& foo(T& t, int szl) {
return t;
}
template <class T> int bar(T& t) {
// But if I scope this as BaseClass::foo(...) it's all good
return foo(t);
}
};
int main() {
int t = 1;
ChildClass c;
c.bar(t);
}
If in bar(...) I call BaseClass::foo(...) the compiler does not complain, but I don't see any ambiguity here and so I'm confused as to why I'd need to do this.
When the compiler tries to match a function name with a function, it does so in two steps. In the first step it finds all the functions that match the given name. If it finds more than one function, it tries the logic of overload resolution to find the best matching function.
In the first step, if the compiler finds a name in the class, it stops looking for functions of the same name in base classes. In your case, since it finds a foo in ChildClass, it stops searching for functions named foo in BaseClass. However, the only matching foo does not match the call and the compiler reports an error.
How to resolve the problem:
Use the method you described in your post. Call BaseClass::foo(...).
Bring all the foo from BaseClass into the scope of ChildClass.
class ChildClass: public BaseClass {
public:
using BaseClass::foo;
template <class T> T& foo(int baz, T& t, int szl) {
return t;
}
template <class T> int bar(T& t) {
return sizeof(foo(1, t)); // Should work.
}
};
From the C++ standard 3.3.10 paragraph 1 :
A name can be hidden by an explicit declaration of that same name in
a nested declarative region or derived class
ChildClass::foo shadows the definition of BaseClass::foo. All you need to bring it into scope is a using directive:
class ChildClass: public BaseClass {
public:
using BaseClass::foo;
template <class T> T& foo(int baz, T& t, int szl);
};
You need a using, like:
using BaseClass::foo;
I can't seem to call a method of a base class without scoping to the base class, and it seems that this is because I have overloaded the method. If I do not overload the method then the compiler doesn't complain. Here's an example of what I'd like to do:
struct BaseClass {
template <typename T> T& foo(T& t) {
return t;
}
};
class ChildClass: public BaseClass {
public:
// The presence of this template causes compiler confusion
template <class T> T& foo(T& t, int szl) {
return t;
}
template <class T> int bar(T& t) {
// But if I scope this as BaseClass::foo(...) it's all good
return foo(t);
}
};
int main() {
int t = 1;
ChildClass c;
c.bar(t);
}
If in bar(...) I call BaseClass::foo(...) the compiler does not complain, but I don't see any ambiguity here and so I'm confused as to why I'd need to do this.
When the compiler tries to match a function name with a function, it does so in two steps. In the first step it finds all the functions that match the given name. If it finds more than one function, it tries the logic of overload resolution to find the best matching function.
In the first step, if the compiler finds a name in the class, it stops looking for functions of the same name in base classes. In your case, since it finds a foo in ChildClass, it stops searching for functions named foo in BaseClass. However, the only matching foo does not match the call and the compiler reports an error.
How to resolve the problem:
Use the method you described in your post. Call BaseClass::foo(...).
Bring all the foo from BaseClass into the scope of ChildClass.
class ChildClass: public BaseClass {
public:
using BaseClass::foo;
template <class T> T& foo(int baz, T& t, int szl) {
return t;
}
template <class T> int bar(T& t) {
return sizeof(foo(1, t)); // Should work.
}
};
From the C++ standard 3.3.10 paragraph 1 :
A name can be hidden by an explicit declaration of that same name in
a nested declarative region or derived class
ChildClass::foo shadows the definition of BaseClass::foo. All you need to bring it into scope is a using directive:
class ChildClass: public BaseClass {
public:
using BaseClass::foo;
template <class T> T& foo(int baz, T& t, int szl);
};
You need a using, like:
using BaseClass::foo;
I'd like to call a function AsJson in a unified way irrespective of whether I'm dealing with an instance or a primitive type, or anything else.
I thought I could define an abstract base class which classes could inherit, and then define a template function that calls the AsJson member function when the template is specialized to appropriate classes. For other types, they could just specialize the template function.
Something like this:
#include <iostream>
class IInstrumented {
public:
virtual void AsJson() const = 0;
};
template <typename T>
void AsJson(const T&);
template <typename T>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
class Foo : public IInstrumented {
public:
void AsJson() const override { std::cout << "A Foo!" << std::endl; }
};
template <>
void AsJson(const int& x) {
std::cout << "An integer!" << std::endl;
}
int main() {
Foo foo;
AsJson(foo);
int x = 3;
AsJson(x);
}
Unfortunately, this results in the following linker error:
special.cpp:(.text+0x56): undefined reference to `void AsJson<Foo>(Foo const&)'
Is this approach workable as is? Is the fix something relatively minor, or is an entirely different approach warranted?
Although there is an accepted answer, I thought I would point out why you had the error in the first place.
When compiling
AsJson(foo);
the compiler is looking for this template instantiation:
template <>
void AsJson(const Foo&);
which is not defined(undefined reference error) because
template <typename T>
void AsJson(const T&);
has no implementation. It is not using this template specialization:
template <>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
because it is not the best candidate while deducting type. By changing the code to:
AsJson<IInstrumented>(foo);
You then explicitly tell the compiler which template specialization you want to use. Previous answer assume all types will inherits from IInstrumented which might not be the case hence why the static_assert checking for type was added.
By providing a default implementation, you can avoid the necessity to inherits from IInstrumentable. Adding an assert in there might help catching serializing unknown types.
You should change
template <typename T>
void AsJson(const IInstrumented& instrumented) {
instrumented.AsJson();
}
to
template <typename T>
void AsJson(const T& instrumented) {
instrumented.AsJson();
}
And then:
$ ./bla
A Foo!
An integer!
Now if you want to ensure that T is derived from IInstrumented. You should add (c++11 only) :
template <typename T>
void AsJson(const T& instrumented) {
static_assert(std::is_base_of<IInstrumented, T>::value,
"T must be a descendant of IInstrumented"
);
instrumented.AsJson();
}
Then with
class Bar {
public:
void AsJson() { std::cout << "A Foo!" << std::endl; }
};
The following code:
Bar bar;
AsJson(bar);
raises:
bla.cpp: In instantiation of ‘void AsJson(const T&) [with T = Bar]’:
bla.cpp:38:13: required from here
bla.cpp:13:3: error: static assertion failed: T must be a descendant of IInstrumented
static_assert(std::is_base_of<IInstrumented, T>::value,
See also How to ensure that the template parameter is a subtype of a desired type?