Template specialisation unable to resolve this-context method without forward-declaration - c++

I have a template class which takes on roughly the form of the given code below.
template <int index = 0>
struct Thing {
void Hello();
void Greet(const char *name);
};
Which works just fine for its purpose, until one decides to try and call methods on itself. Say that you define the methods as shown below.
template <>
void Thing<>::Greet(const char *name) {
Hello();
printf(", %s!\n", name);
}
template <>
void Thing<>::Hello() {
printf("Hello");
}
Then calling Greet() yields an explicit specialisation error, which I find weird. The compiler ought to know all of the methods of this by its interface declaration, but for some reason it cannot resolve it in this case. This can be solved in one of two ways; either you must forward-declare any methods called on yourself, or make sure the methods are defined in an order that ensures that the ones you call are defined beforehand.
This issue is really annoying, because I have provided an interface declaration, and any specialisation of the template ought to conform to that same interface, so I really don't see why the compiler complains about this — is there any way to fix the issue without polluting the code with forward-declarations of each method, or having to order methods in a particular manner?
I hope you have some great ideas for solving the issue in a better way. Thank you in advance!
For ease of reproducing the issue, here's a snippet that will call the offending method.
int main(int argc, const char *argv[]) {
Thing<0> thing_with_zero;
thing_with_zero.Greet("World");
return 0;
}

You might specialize the whole class to ensure that specialization are seen:
// Primary template
// generic one
template <int index = 0>
struct Thing {
void Hello() {/*..*/}
void Greet(const char *name) {/*..*/}
};
// Specialization for index == 0
template <>
struct Thing<0> {
void Greet(const char *name) {
Hello();
printf(", %s!\n", name);
}
void Hello() {
printf("Hello");
}
};
Alternatively, you might drop specialization with the help of if constexpr (C++17):
template <int index = 0>
struct Thing
{
void Greet(const char *name) {
if constexpr (index == 0) {
Hello();
printf(", %s!\n", name);
} else {
// ...
}
}
void Hello() {
if constexpr (index == 0) {
printf("Hello");
} else {
// ...
}
}
};

Related

Default argument with template in C++

I'm designing an interface, by which users can define a class that tells what they want to do.
The code is something like the following,
#include <stdio.h>
class Dummy{
public:
void do(){ printf("do nothing\n"); }
};
class Task{
public:
void do(){ printf("do something\n"); }
};
template <class TASK>
void func(TASK &task = Dummy()){
task.do();
}
int main(){
func(); // do nothing
Task task;
func(task); // do something
}
How to make it work?
The main issue is this func argument:
TASK &task = Dummy()
It will not work unless it is const. This happens because non-const lvalue reference to type cannot bind to a temporary.
But if you can use const there, you can easily solve your problem:
class Dummy{
public:
void doit() const { printf("do nothing\n"); }
};
class Task{
public:
void doit() const { printf("do something\n"); }
};
template <class TASK = Dummy>
void func(const TASK &task = TASK()){
task.doit();
}
int main(){
func(); // do nothing
Task task;
func(task); // do something
}
For starters, don't have an identifier (function named) named do, since do is a language keyword. Using it as an identifier is a diagnosable error. There's no option other than changing the name of the function.
Second, the argument will of func() will need to be const, since the default value being passed is a temporary (which can only be bound to a const reference). This also means your function in the classes needs to be const qualified.
Third, when calling func() it is necessary to either pass SOME information so the compiler can work out how to instantiate the template. If you want to pass no information at all at the call site (i.e. func()) then you need to have a non-templated overload.
Fourth, use C++ streams rather than C I/O.
class Dummy
{
public:
void do_it() const { std::cout << "do nothing\n"; }
};
class Task
{
public:
void do_it() const { std::cout << "do something\n"; }
};
template <class TASK>
void func(const TASK &task)
{
task.do_it();
}
void func()
{
func(Dummy());
}
int main()
{
func(); // do nothing
Task task;
func(task); // do something
}
Option 2 is to replace the two versions of func() above with
template <class TASK = Dummy>
void func(const TASK &task = TASK())
{
task.do_it();
}

Making a tuple style class that optimizes out unused sections

This is more of a question of how the C++ compiler handles const typeid calls.
Hello! I am trying to make a tuple-style class, configured in such a way that I don't have to rewrite a bunch of the code with specializations.
So this is the general idea:
struct null_type{};
template <typename T1,typename T2=null_type,typename T3=null_type>
class ptestclass
{
private:
template<typename K1,typename K2,typename K3>
class barclass
{
public:
static inline void bar(std::tuple<K1,K2,K3>& vals,K1* otherval1,K2* otherval2,K3* otherval3)
{
Foo(tr1::get<0>(vals),*otherval1);
Foo(tr1::get<1>(vals),*otherval2);
Foo(tr1::get<2>(vals),*otherval3);
}
};
template<typename K1,typename K2>
class barclass<K1,K2,null_type>
{
public:
static inline void bar(std::tuple<K1,K2,null_type>& vals,K1* otherval1,K2* otherval2,null_type* otherval3)
{
Foo(tr1::get<0>(vals),*otherval1);
Foo(tr1::get<1>(vals),*otherval2);
}
};
template<typename K1>
class barclass<K1,null_type,null_type>
{
public:
static inline void bar(std::tuple<K1,null_type,null_type>& vals,K1* otherval1,null_type* otherval2,null_type* otherval3)
{
Foo(tr1::get<0>(vals),*otherval1);
}
};
/*
*Old Bar function...much more readable than bar class, but you cannot partially specialize
*member functions of a class
*
void inline bar(std::tuple<T1,T2,T3> otherval)
{
if (typeid(T1) != typeid(null_type))//constant check hopfully optomized out
{
Foo(vals.get(1),otherval.get(1));
}
if (typeid(T2) != typeid(null_type))//constant check hopfully optomized out
{
Foo(vals.get(2),otherval.get(2));
}
if(typeid(T3) != typeid(null_type))//constant check hopfully optomized out
{
Foo(vals.get(3),otherval.get(3));
}
}
*/
std::tuple<T1,T2,T3> vals;
template<typename K>
void static inline Foo(K& val,K& otherval)
{
//inlineable, short function that is called many (millions) of times per iteration
val += otherval;
}
template<>
void inline Foo<null_type>(null_type& val,null_type& otherval)
{
//inlineable, short function that is called many (millions) of times per iteration
throw "Foo called on null type";
}
public:
ptestclass()
{
printf("made object");
}
void one_iteration(T1* otherval1,T2* otherval2,T3* otherval3,size_t count)
{
for (int i = 0; i < count; ++i)
{
barclass<T1,T2,T3>::bar(vals,otherval1+i,otherval2+i,otherval3+i);
}
}
};
//exposed public class with specialized one_iteration interfaces
template <typename T1,typename T2=null_type,typename T3=null_type>
class testclass : public ptestclass<T1,T2,T3>
{
public:
void one_iteration(T1* otherval1,T1* otherval2,T1* otherval3,size_t count)
{
ptestclass::one_iteration(otherval1,otherval2,otherval3,count);
}
};
template <typename T1>
class testclass<T1,null_type,null_type> : public ptestclass<T1,null_type,null_type>
{
public:
void one_iteration(T1* otherval1,size_t count)
{
ptestclass::one_iteration(otherval1,NULL,NULL,count);
}
};
So my question is is this optimization even possible within C++? If not, it will probably make more sense for me to use an inheritance model on the child nodes rather then a template at this level. However, I am trying to avoid the continual check of the number of types specified and the cost of indirection.
I'm going to start diving into the assembly to see if that is what the compiler does...Just in case this is not standardized behavior, I'm using the Microsoft Visual C++ Compiler 10.0.
I think I misunderstood your question when I put my earlier comment.
Assuming you can use c++11, or you can use boost, you could use something like !std::is_same< T1, null_type >::value /*or boost::is_same...*/ instead of typeid(T1) != typeid(null_type). This uses TMP to resolve to a compile-time constant, which most compilers would have no trouble optimizing away.
This is more of a question of how the C++ compiler handles const typeid calls.
I didn't answer this specific question, but if I understand what you were actually looking for, the above should suffice.

Explicit template specialization

I hate to ask such a general question, but the following code is a exercise in explicit template specialization. I keep getting the error:
c:\users\***\documents\visual studio 2010\projects\template array\template array\array.h(49): error C2910: 'Array::{ctor}' : cannot be explicitly specialized
#ifndef ARRAY_H
#define ARRAY_H
template <typename t>`
class Array
{
public:
Array(int);
int getSize()
{
return size;
}
void setSize(int s)
{
size = s;
}
void setArray(int place, t value)
{
myArray[place] = value;
}
t getArray(int place)
{
return myArray[place];
}
private:
int size;
t *myArray;
};
template<typename t>
Array<t>::Array(int s=10)
{
setSize(s);
myArray = new t[getSize()];
}
template<>
class Array<float>
{
public:
Array();
};
template<>
Array<float>::Array()
{
cout<<"Error";
}
#endif
Thanks
The implementation of the specialization's constructor isn't a template! That is, you just want to write:
Array<float>::Array()
{
std::cout << "Error";
}
Actually, it seems that you want to restrict the use of your 'Array' class template to not be used with 'float' in which case you might want to only declare but not define you specialization to turn the run-time error into a compile-time error:
template <> class Array<float>;
Of course, there are many variations how you can prevent instantiation of classes. Creating a run-time error seems to be the worst option, however.

instantiating a free template function within a template class

I need to instantiate a free template function (FTF) within a template class (TC). The FTF takes as a template parameter one of the template parameters of the TC. The TC also holds generic pointers to these FTF's, and these functions are called through the pointers.
The step of taking a pointer to a FTF is not enough to instantiate it, and I receive linker errors from the GCC toolchain. MSDN illustrates FTF specification as so -- however my instantion of the FTF is dependant on a template parameter of my TC, and therefore the FTF instantiation cannot be placed in free scope.
Is this possible ? I am attaching some basic generated code, the issue is in the constructor of the class test_service, where I assign the pointer of a free function into a custom container. I get a linker error telling me the free function cannot be found (uninstantiated). I know that specifying a call to the template function in the class somewhere will produce a instantiation, however I am only going to be making a call via a pointer.
#include "rpc_common.h"
#include <boost/cstdint.hpp>
namespace rubble { namespace rpc {
struct test_service_dummy_tag{};
template<typename T>
class test_service_skel
{
public:
bool Init() {}
bool TearDown() {}
bool test_one(TestRequest,TestResponse){};
private:
};
template<typename T_IMPL>
bool test_service_test_one(T_IMPL & impl,ClientRequest & request)
{
return 0;
}
template<typename T_IMPL=test_service_skel<test_service_dummy_tag> >
class test_service
{
public:
test_service()
{
// uncomment the following two lines and a instantiation will occur.
// ClientRequest cr;
//test_service_test_one<T_IMPL>(m_impl,cr);
m_dispatch_table.SetEntry( Oid("test_one",0),(void *) & test_service_test_one<T_IMPL>);
}
bool Init() { return m_impl.Init(); };
bool TearDown() { return m_impl.TearDown(); };
private:
T_IMPL m_impl;
OidContainer<Oid,void *> m_dispatch_table;
};
} }
EDIT: self-contained minimal version
class test_skel
{
bool test_function()
{
return true;
}
};
template<typename T>
bool test_function()
{
}
template<typename T = test_skel>
class test
{
public:
test()
{
dispatch = (void *) & test_function<T>;
}
void * dispatch;
};
int main()
{
test<> t;
return 0;
}
There is no problem iff you don't use a void*, i.e.: http://www.ideone.com/eRgUG
However, if you insist on storing the pointer in a void*, then you need to take the address using a specific function pointer first and then cast - e.g.
bool (*temp)() = &test_function<T>;
dispatch = reinterpret_cast<void*>(temp); // YUCK
This gives the compiler enough context to generate the address for you.
Ahh - just saw DeadMG's answer, the function to generate the void* is neater...
Your self-contained example wouldn't compile for me with a strange error about overloaded functions, when there is no overloading going on, with MSVC. I did, however, manage to work around it.
class test_skel
{
bool test_function()
{
return true;
}
};
template<typename T> void* to_void_pointer(T t) {
return reinterpret_cast<void*>(t);
}
template<typename T>
bool test_function()
{
return true;
}
template<typename T = test_skel>
class test
{
public:
test()
{
dispatch = to_void_pointer(&test_function<T>);
}
void * dispatch;
};
int main()
{
test<> t;
return 0;
}
This compiles cleanly. I suspect that whatever behaviour you're seeing and I saw is a compiler error.

default template arguments in c++

Suppose i have a function template StrCompare
template<typename T=NonCaseSenCompare>//NonCaseSenCompare is a user defined class look at the detailed code below.
int StrCompare(char* str1, char* str2)
{
...
}
now in the main function i write a line
char* str1="Zia";
char* str2="zia";
int result=StrCompare(str1,str2);
it should work because we have provided a default template argument, but it does'nt compiler gives the following error
no matching function for call to `StrCompare(char*&, char*&)'
Now the detailed code is given by
#include<iostream.h>
class CaseSenCompare
{
public:
static int isEqual(char x, char y)
{
return x==y;
}
};
class NonCaseSenCompare
{
public:
static int isEqual(char x,char y)
{
char char1=toupper(x);
char char2=toupper(y);
return char1==char2;
}
};
template<typename T=NonCaseSenCompare>
int StrCompare(char* str1, char* str2)
{
for(int i=0;i < strlen(str1)&& strlen(str2);i++)
{
if(!T::isEqual(str1[i],str2[i]))
return str1[i]-str2[i];
}
return strlen(str1)-strlen(str2);
}
main()
{
char* ptr1="Zia ur Rahman";
char* ptr2="zia ur Rahman";
int result=StrCompare(ptr1,ptr2);//compiler gives error on this line
cout<<result<<endl;
system("pause");
}
If I write
int result=StrCompare<>(ptr1,ptr2);
compiler gives the same error message.
As gf and AndreyT already wrote, you can't have default template arguments with function templates. However, if you turn your comparators into function objects, you can still use default function arguments:
template<typename Comp>
int StrCompare(char* str1, char* str2, Comp = NonCaseSenCompare())
{
...
}
You can now call StrCompare() like this
StrCompare("abc","aBc",CaseSenCompare());
or like this:
StrCompare("abc","aBc"); // uses NonCaseSenCompare
A comparator would then have to look like this:
struct CaseSenCompare {
bool operator()(char x, char y) const {return x==y;}
};
Adjust StrCompare() accordingly.
§14.1/9:
A default template-argument shall not
be specified in a function template
declaration or a function template
definition, nor in the
template-parameter-list of the
definition of a member of a class
template.
A simple work-around would be to move it into a class:
template<typename T=NonCaseSenCompare>
struct StrCompare {
static int compare(char* str1, char* str2) { /* ... */ }
};
Firstly, function templates do not support default template arguments, only class templates do.
Secondly, even when all class template parameters have default arguments, you still have to specify an empty <> to refer to that class template.
What i use is next trick;
lets say you want to have function like this
template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
E one(1);
array.add( one );
}
you will not be allowed, but i do next way:
template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
/*static - as you wish */ ARR_E* parr_;
void doStuff(); /* do not make this one static also, MSVC complains */
};
template <typename E, typename ARR_E>
void worker::doStuff<E, ARR_E>::getChunks()
{
E one(1);
parr_->add( one );
}
so this way you may use it like this.
MyArray_t my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();
as we can see no need to explicitly set second parameter.
maybe it will be useful for someone.