I have a base class with a template member, which is explicitly specialized for some cases. A derived class further specializes the template member of the base class. The rationale beyond that is that the various specialization of the template member do "logically" the same job, adapting to a specific situation. The base class provides some template specializations, which do the task for some cases, a derived class should "extend" the same task to other cases, by further specializing the template member.
Here is a minimal example to illustrate the problems I encounter.
#include <iostream>
struct A {
template <int i>
void dosomething();
void show();
};
template<>
void A::dosomething<0>()
{
std::cout << "0 in A" << std::endl;
}
void A::show()
{
dosomething<0>();
}
struct B : A {
// Without the following declaration:
// error: template-id ‘dosomething<1>’ for ‘void B::dosomething()’
// does not match any template declaration
template <int i>
void dosomething();
};
template<>
void B::dosomething<1>()
{
std::cout << "1 in B" << std::endl;
}
int main()
{
A x;
x.dosomething<0>();
B y;
y.dosomething<0>(); // Link error!
y.show();
y.dosomething<1>();
return 0;
}
The template member A::dosomething() is explicitly specialized for i=0 in the base class. The code for the template is explicitly generated, and called in the member A::show().
The first problem I found are:
A) Without a duplicated declaration
template <int i>
void dosomething();
inside the definition of B, the code does not compile, with the error:
template-id ‘dosomething<1>’ for ‘void B::dosomething()’
does not match any template declaration.
Why is the previous declaration in the base class A not visible?
B) The code above gives rise to a link error:
undefined reference to `void B::dosomething<0>()'
The error is due to the call y.dosomething<0>() in main. It can be avoided by calling instead y.A::dosomething<0>(). Why is dosomething<0>() apparently invisible in an instance of B?
When you do out-of-line definition of a member function the declaration of that function is looked up in the class that is referred before the :: operator.
Consider this:
struct C { void test(); };
struct D : C { };
void D::test() { }; // error, test is not a member of D but of C
this is the same of doing
template<> void B::dosomething<1>()
{ }
dosomething and all it specialization definitions must be qualified by the class it was declared on, i.e in A as the way you did with dosomething<0>.
Also notice that the declaration of dosomething in B is totally unrelated to that of A. you're getting a link error because of the call to a non defined specialization B::dosomething<0>.
You can create the specialization template<> void A::dosomething<1>(){ } but you're not getting the polymorphic behavior that you're expecting, A::dosomething<1> will be shared by all the derived class, if you really need differents versions of dosomething<1> by subclasses, you are confined to the initial repetition, and in order to access A::dosomething<0> from B you do it as static_cast<A&>(b).dosomething<0>().
You also should take a look at static polymorphism in this answer
Related
This question already has answers here:
C++ compiler does not check if a method exists in template class
(2 answers)
Closed 6 months ago.
This code compiles and works only when calling CRTP's methods:
template <typename CRTP>
struct crtp
{
// using type = typename CRTP::type; // will not compile
void print() const
{
using type = typename CRTP::type; // compiles
static_cast<const CRTP&>(*this).print();
}
};
#include <iostream>
int main()
{
struct foo : crtp<foo>
{
using type = int;
void print() const {
std::cout << "John Cena\n";
};
} f{};
const crtp<foo>& c = f;
c.print();
return 0;
}
Upon crtp<foo>'s instantiation, foo is incomplete. But its methods can be used.
However, CRTP's types can't used dues CRTP's incompleteness outside functions.
Why is it allowed to defer a check for incompletness until a class's function is called, but not upon object's instatiation?
When using CRTP::type within the class body (uncommented), one will get a compilation error:
<source>:4:33: error: no type named 'type' in 'foo'
using type = typename CRTP::type; // will not compile
~~~~~~~~~~~~~~~^~~~
Note that even if you write a member function definition inside the class template definition, it is not instantiated when the class template is instantiated.
foo can't be defined until its base class crtp<foo> has been completely defined, but the type alias in the definition of crtp<foo> requires foo's definition to be known, which requires crtp<foo> to already be defined, which requires foo, and so on...
Member functions, on the other hand, are not compiled until after their class has been fully defined, so the type alias is fine there.
(That is, instantiating void crtp<foo>::print() const works because it only happens after foo has been defined and crtp<foo> has been instantiated.)
Separating the class template definition from the member function definition makes the two phases clearer:
template <typename CRTP>
struct crtp
{
// Can't compile unless CRTP is defined at this point.
using type = typename CRTP::type;
void print() const;
};
template <typename CRTP>
void crtp<CRTP>::print() const
{
// Also can't compile unless CRTP is defined at this point, but
// this is a later point than the class definition.
using type = typename CRTP::type;
static_cast<const CRTP&>(*this).print();
}
Within CRTP, derived classes are incomplete:
struct foo : crtp<foo> // foo incomplete here
{
using type = int;
void print() const {
// foo complete here
std::cout << "John Cena\n";
}
}; // foo complete here
foo is also complete in void crtp<foo>::print() const.
We all know a C++ class template does not generate member functions that are not used, as illustrated below:
template<typename T>
class A
{
public:
void WrongFunction(T t);
void RightFunction(T t);
};
template<typename T>
void A<T>::WrongFunction(T t)
{
std::vector<T> v;
auto a = "abc" - v;
}
template<typename T>
void A<T>::RightFunction(T t)
{
std::cout << "Success" << std::endl;
}
int main()
{
A<int> a;
a.RightFunction(2);
//a.WrongFunction(1);
return 0;
}
Since the WrongFunction is not called in main, there's no actual code generated for it and therefore no compilation error occurs.
Now, let's introduce an abstract base class that defines the interface for class A(basically, template inheritance):
template<typename T>
class Base
{
public:
virtual void RightFunction(T t) = 0;
virtual void WrongFunction(T t) = 0;
};
template<typename T>
class A : Base<T>
{
public:
void WrongFunction(T t) override;
void RightFunction(T t) override;
};
template<typename T>
void A<T>::WrongFunction(T t)
{
std::vector<T> v;
auto a = "abc" - v;
}
template<typename T>
void A<T>::RightFunction(T t)
{
std::cout << "Success" << std::endl;
}
int main()
{
A<int> a;
a.RightFunction(2);
//a.WrongFunction(1);
return 0;
}
Suddenly, the compiler refuses to work:
prog.cc: In instantiation of 'void A::WrongFunction(T) [with T =
int]': prog.cc:39:1: required from here prog.cc:24:20: error: no
match for 'operator-' (operand types are 'const char [4]' and
'std::vector >')
auto a = "abc" - v;
My understanding of the work flow is, in main, I say create an instance of A. Fine, the compiler then finds the template declaration for A(note that A is not a class; A<SomeType> is.). Wow, it depends on Base<int>. Fine, the compiler then finds the template declaration for Base, plugs int into the position held by T - now we have the declaration for the class Base<int>, but no definition is generated - after all, we did not provide a template for definition generation for Base<SomeType>, and no one has ever created any instance of Base<int> or has called a function on the instance. That's fine. Then the compiler extends the declaration of Base<int> and generates the declaration of A<int>. Wait, on the next line, RightFunction is called. So the compiler finds the template definition for RightFunction for A and plugs in the specific type int and generates the member function definition for A.
Since WrongFunction is never called(no specialization involved either; no explicit instantiation either), the compiler shouldn't even try to generate the code for A<int>::WrongFunction --- my question is, what the heck is going on?
Compiler: gcc 4.9.2
Thanks.
From N3337, §14.7.1/10 [temp.inst]
An implementation shall not implicitly instantiate a function template, a member template, a non-virtual member function, a member class, or a static data member of a class template that does not require instantiation. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated. ...
So it's legal for an implementation to instantiate the virtual member function even though you never call it.
In practice, this is likely to always be the case because when instantiating a class template the compiler also needs to instantiate the vtable for that class which must be filled with the addresses of virtual member functions.
I'm seeing an error related to templates (compiler is Visual Studio 2012) that I don't understand. Here's the code, boiled down to the essentials:
// Templated class - generic
template <typename T>
class Test
{
public:
void WorksFine() {} // Comiples and works as expected at runtime
void Problem();
};
// Templated class - expicit specialization for T = int.
template <>
class Test<int>
{
public:
void WorksFine() {} // Comiples and works as expected at runtime
void Problem();
};
// The definition below compiles and works fine at runtime.
template<typename T> void Test<T>::Problem() {}
// The definition below gives error C2910.
template<> void Test<int>::Problem() {printf("In Test::Problem(int instantiation)\n");}
For the WorksFine method, the function definition is inside the explicitly specialized class definition, and everything is fine. But for the Problem method, when I define the method outside the explicitly specialized class definition, I get error C2910
Why is this? Error C2910 indicates that the problem is that Test::Problem() is already defined. But it is not defined inside the class...there is no function definition only a declaration.
It seems pretty lame to be able to do something or not depending on where you choose to put the function definition, which I always though was more of a style/syntax decision, not a functionality/semantics decision. Am I missing something?
You don't need the template<>. Just write:
void Test<int>::Problem() {printf("In Test::Problem(int instantiation)\n");}
The template<> syntax on a member specialization is required where explicitly instantiating a member on its own; it is omitted when defining a member of an already existing specialization.
template<typename T> struct X { static int i; };
template<> int X<int>::i = 0; // member instantiation, uses template<>
template<typename T> struct Y { static int i; };
template<> struct Y<int> { static int i; } // template specialization
int Y<int>::i = 0; // no template<>
You don't need template anymore in the explicit function definition: void Test<int>::Problem() {printf("In Test::Problem(int instantiation)\n");}
In this case g++ gives a slightly better error message error: template-id 'Problem<>' for 'void Test<int>::Problem()' does not match any template declaration
Try this:
// The definition below gives error C2910.
void Test<int>::Problem()
{
printf("In Test::Problem(int instantiation)\n");
}
int main()
{
Test<int> hey;
hey.Problem();
return 0;
};
I would like to have several different function definitions for a member function in a templated class. Something like this:
template <typename T>
class MyClass
{
void Foo();
T val;
//other functionality and data
};
//handles all types
template <typename T>
void MyClass<T>::Foo()
{
return val;
}
//handles a special type in a different way
template <>
void MyClass<float>::Foo()
{
return val + 5.0f;
}
I've tried implementing this as above and get a linker error for every special type I try to explicitly instantiate. The linker error mentions that the function has already been previously defined. Maybe I'm looking in the wrong places but I couldn't find any resources to help me figure out this problem :(
Q. Is this possible? If so, how do you do this and why does it work?
Thanks!
Here is a workaround that I frequently use. As it as been said before, you have to specialize the complete template. The idea is to make the method you want to specialize a static member of some struct (that should be nested and private for encapsulation reasons). Like this:
template< typename T >
class MyClass {
struct PerformFoo {
static void doFoo () {
std::cout << "Foo for general type" << std::endl;;
}
};
public:
void Foo () {
PerformFoo::doFoo();
}
};
template<>
struct MyClass< float >::PerformFoo {
static void doFoo () {
std::cout << "Foo for float" << std::endl;;
}
};
Now in your main, the code
MyClass< int > myInt;
myInt.Foo();
MyClass< float > myFloat;
myFloat.Foo();
prints
Foo for general type
Foo for float
on your terminal. By the way: this does not involve any performance penalty with modern compilers. Hope this helps you.
By defining the specialized member function as inline function you will get rid of the link error complaining the specialized member function having been defined elsewhere.
//handles a special type in a different way
template <>
inline void
MyClass<float>::Foo()
{
return val + 5.0f;
}
The reason being that a specialized function is no longer a function template, but a concrete function. Therefor it will be compiled several times when compiling source files that includes this header file which is why you get the "already defined" error.
Another solution is to move the implementation of the specialized function out of the header file and put it into the source file, meanwhile, declare the specialized function in the header file. Note that the declaration of the specialized member function must stay outside of the class definition:
/// Declare the specialized function in the header file but outside the
/// class definition.
template <> void MyClass<float>::Foo()
/// Define the specialized function in .cpp file:
template <>
void
MyClass<float>::Foo()
{
return val + 5.0f;
}
I've tried implementing this as above and get a linker error for every special type I try to explicitly instantiate.
What does that mean? If you explicitly specialize the template you cannot explicitly instantiate it anymore for the same template arguments. The whole purpose of an explicit specialization is to prevent the instantiation of it (which is a generated specialization) in favor of your explicit specialization.
So your description does not make sense to me. Just remember that you need to put definitions of templates and member functions of class templates in the header instead of in the .cpp file if you want to instantiate them implicitly. And that explicit specializations need to be declared to everyone who uses their template with their arguments.
// put this specialization into the header, for everyone to see
template <> void MyClass<float>::Foo();
It is not possible. When you specialize a template, you must specialize the entire template, which in this case means the entire class.
You can make foo a template function inside the template class. It is not exactly the same as what you are asking for, but it might meet your needs.
Update:
template<typename T> class Foo {
public:
template<typename R> void foo() {printf("This is foo\n");}
template<> void foo<float>() {printf("This is foo<float>\n");}
};
Or:
template<typename T> class Foo {
public:
template<typename R> void foo() {printf("This is foo\n");}
//template<> void foo<float>() {printf("This is foo<float>\n");}
};
template<> template<> void Foo<float>::foo<float>() {
printf("This is foo<float>\n");
}
along with:
int main(int argc,char * argv[])
{
Foo<int> iFoo;
iFoo.foo<int>();
Foo<float> fFoo;
fFoo.foo<float>();
return 0;
}
generates:
This is foo
This is foo<float>
The syntax for calling foo is a bit awkward.
If I want to use a member of a template base class from a template derived class, I have to bring it into scope as such:
template <typename T>
struct base
{
void foo();
};
template <typename T>
struct derived : base<T>
{
using base<T>::foo;
};
Why can't I place this using statement into a local scope, like other using statements?
template <typename T>
struct base
{
void foo();
};
template <typename T>
struct derived : base<T>
{
void f()
{
using base<T>::foo; // ERROR: base<T> is not a namespace
}
};
The purpose of using base<T>::foo in the function scope is that you want to call foo in the function, and since it gives error, you cannot do that.
If you want to call the functon (otherwise why you would do that), then you can do these which are allowed:
this->template base<T>::foo(); //syntax 1
this->base<T>::foo(); //syntax 2 - simple
this->foo(); //syntax 3 - simpler
However, you cannot write this:
foo() ; //error - since foo is in base class template!
//if you write `using base<T>::foo` at class scope, it will work!
Demo at ideone : http://www.ideone.com/vfDNs
Read this to know when you must use template keyword in a function call:
Ugly compiler errors with template
The standard (draft 3225) says in [namespace.udecl]:
A using-declaration for a class member shall be a member-declaration. [ Example:
struct X {
int i;
static int s;
};
void f() {
using X::i; // error: X::i is a class member
// and this is not a member declaration.
using X::s; // error: X::s is a class member
// and this is not a member declaration.
}
— end example ]
A using-directive has no such restriction, however ([namespace.udir]):
when looking up a namespace-name in a using-directive, only namespace names are considered