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.
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.
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
Ok, I am working with g++ 4.8.2 and have the following (somewhat long) code that gets an error message about an incomplete type. I have reduced the code to a smaller chunk for inclusion here and can be compiled directly:
#include <cstdlib>
struct S
{
void method(){}
};
template<size_t sz, size_t tot>
class genpool
{
};
template <class T>
class mempool
{
private:
genpool<sizeof(T), 10*sizeof(T)> p;
};
template <class obj, class mthd>
class functor
{
private:
static mempool<functor<obj, mthd> > pool;
};
template <class obj, class mthd>
mempool<functor<obj, mthd> > functor<obj, mthd>::pool;
int main()
{
typedef void (S::*m)();
typedef functor<S, m> fctr;
fctr f;
}
The compiler error message is:
g++ jj.C
jj.C: In instantiation of ‘class mempool<functor<S, void (S::*)()> >’:
jj.C:30:30: required from ‘class functor<S, void (S::*)()>’
jj.C:37:8: required from here
jj.C:18:17: error: invalid application of ‘sizeof’ to incomplete type ‘functor<S, void (S::*)()>’
genpool<sizeof(T), 10*sizeof(T)> p;
^
Compilation exited abnormally with code 1 at Thu Apr 9 18:50:06
Obviously, the template functor is defined above and all the arguments to functor have been explicitly defined. This seems to imply to me that the sizeof function should be well defined. Is there something that I am missing here?
--Ron
The problem is that the compiler is attempting to instantiate mempool<> before it instantiates functor<>. This is because the compiler feels it needs to be able to define the static member functor<>::pool first before functor<> itself is considered fully defined.
A workaround is to return a mempool<> & from a static member function.
template <class obj, class mthd>
class functor
{
private:
static mempool<functor> & get_pool () {
static mempool<functor> pool;
return pool;
}
};
//template <class obj, class mthd>
//mempool<functor<obj, mthd> > functor<obj, mthd>::pool;
This works because the reference means it is okay for mempool<> to remain incomplete until after functor<> is instantiated. (Actually, a method of a template is not instantiated unless there is code that actually calls it.) When the static method is called, functor<> itself is complete, so the static object within functor<>::get_pool can be properly instantiated.
As a side note, it is acceptable to pass an incomplete type as an argument to a template, but the template has restrictions on what it can actually do with an incomplete type. Everything is fine if the template only requires a reference or pointer to the type for its instantiation.
Your definition of functor is recursive. It requires the compiler to know the size of the type of functor while determining the functor type. You can generate exactly the same problem with this code:
template <class A>
class B {
public:
static const int szA = sizeof(A);
};
template <class A>
class C {
public:
static B<C<A> > b;
};
int main() {
C<int> c;
}
Depending on what your application is, you should be able to do what you want using type traits.
At the time you declare pool inside of functor, you are still defining the functor class, so the type functor is still incomplete.
This is similar to forward declarations:
class functor;
functor func; <<-incomplete here
functor *ptr; <<-pointer is fine
class functor
{
functor func; <<-incomplete again
};
functor func; <<-now complete definition, ok
I don't think you can do it, because you have a recursive definition. For example, you can't do this:
#include <cstdlib>
class B;
class A
{
B b;
};
class B
{
A a;
};
int main()
{
A x;
}
The only way out is to make one of the members a pointer instead of an instance.
I wrote a function template to "convert"/repack a boost::shared_ptr<T> to a std::shared_ptr<T> and vice versa by following this proposal. It's working fine unless I have a boost::shared_pt<T> and the type of T is an abstract class.
What I figured out so far is, that the problem occurs when boost/shared_ptr.hpp and boost/shared_array.hpp are included together. If only boost/shared_ptr.hpp is included it's working when the type of T is an abstract class.
I'm using clang 3.3 and boost 1.55.0 . It would be great if someone could tell my why it's not working and how to get it working.
Thanks for your help
Here is a minimal example:
//main.cpp
#include <boost/shared_array.hpp> //removing this include and it's working
#include <boost/shared_ptr.hpp>
#include <memory>
template<typename SharedPointer> struct Holder {
SharedPointer p;
Holder(const SharedPointer &p) : p(p) {}
Holder(const Holder &other) : p(other.p) {}
Holder(Holder &&other) : p(std::move(other.p)) {}
void operator () (...) const {}
};
template<class T>
std::shared_ptr<T> to_std_ptr(const boost::shared_ptr<T> &p)
{
typedef Holder<std::shared_ptr<T>> H;
if(H *h = boost::get_deleter<H, T>(p)) // get_deleter seems to cause the problem
{
return h->p;
}
else
{
return std::shared_ptr<T>(p.get(), Holder<boost::shared_ptr<T>>(p));
}
}
Here the code I used to test it:
//main.cpp
template<typename T> class Base
{
public:
T value;
virtual void abstract() = 0;
virtual ~Base() {}
};
template<typename T> class Derived : public Base<T>
{
public:
virtual void abstract() override {}
virtual ~Derived() {}
};
int main(int argc, const char * argv[])
{
boost::shared_ptr<Base<int>> ptr{new Derived<int>()};
// error here
std::shared_ptr<Base<int>> a = to_std_ptr(ptr);
// no error here
std::shared_ptr<Base<int>> b = to_std_ptr(boost::static_pointer_cast<Derived<int>>(ptr));
return 0;
}
Here's the error message(shortened):
boost/smart_ptr/shared_array.hpp:111:102: error: array of abstract class type 'Base<int>'
shared_array( shared_array<Y> const & r, typename boost::detail::sp_enable_if_convertible< Y[], T[] >::type = boost::detail::sp_empty() )
main.cpp:64:40: note: in instantiation of template class 'boost::shared_array<Base<int> >' requested here
if(H *h = boost::get_deleter<H, T>(p))
main.cpp:86:36: note: in instantiation of function template specialization 'to_std_ptr<Base<int> >'requested here
std::shared_ptr<Base<int>> i = to_std_ptr(ptr);
main.cpp:23:18: note: unimplemented pure virtual method 'abstract' in 'Base'
virtual void abstract() = 0;
What I get from the error message is that the compiler tried to create an array of abstract classes what of course doesn't work. But why is he even trying to do so and what has boost/sharred_array to do with that. Is he maybe picking the wrong overload for boost::get_deleter?
Here's a small example of where the error comes from:
struct abstract
{
virtual void foo() = 0;
};
template<class X, class Y>
struct templ {};
template<class T>
struct bar
{
template<class U>
bar(templ<U[], T[]>) {} // (A)
};
int main()
{
bar<abstract> x;
}
It seems even to be illegal to form the type array of [abstract-type] in (A), therefore instantiating the class template bar with the argument abstract makes the program ill-formed.
The same thing is happening in the background for shared_array. But why is shared_array<Base> instantiated?
The free function boost::get_deleter is overloaded, shared_array.hpp adds an overload to the overload set (actually, adds a template):
template< class D, class T > D * get_deleter( shared_array<T> const & p );
Before overload resolution, even before finding out which functions are viable, function templates need to be instantiated. Instantiating this get_deleter template above leads to instantiating shared_array<Base>, which leads to the program being ill-formed.
The solution is, not to let the above get instantiated: Don't supply the template parameter T, it can't deduce the T in shared_array<T> from a shared_ptr<T>: the two types are unrelated.
Change
if(H *h = boost::get_deleter<H, T>(p))
to
if(H *h = boost::get_deleter<H>(p))
and it works.
Explanation why letting T be deduced works:
When writing a function call where a function template could be meant (looking at the name called), the template parameters have to be set. You can supply them explicitly (inside <> as in get_deleter<H, T>). If you don't supply all of them explicitly (as in get_deleter<H>), the remaining ones have to be deduced from the arguments of the function call.
After all template parameters have been either set explicitly or deduced, their occurrences in the function template are substituted. The error when using get_deleter<H, Derived> occurs in this step: the substituted get_deleter looks like this:
template<> H * get_deleter( shared_array<Derived> const & p );
Here, shared_array<Derived> needs to be instantiated. But during this instantiation, the error explained above occurs. (Note: it's not in the immediate context of get_deleter, therefore SFINAE doesn't apply.)
Now, when you don't explicitly supply the second template parameter, it has to be deduced. And this deduction fails for the function template
template< class D, class T > D * get_deleter( shared_array<T> const & p );
if you use an argument expression of type shared_ptr<Derived>. As deduction fails, no instantiation takes place of the function template, and therefore no instantiation of shared_array<Derived> (deduction fails = some template parameters could not be set).
Why does deduction fail? The compiler needs to deduce the template parameter T inside the function parameter type shared_array<T> const& from the argument expression, which is of the type shared_ptr<Derived>. But this deduction can only succeed (with few exceptions) when the function parameter type can be made equal to the argument expression type. I.e., it can only succeed if there's some type X, such that shared_array<X> is the same type as shared_ptr<Derived>. But there isn't: the specializations of shared_ptr and shared_array are unrelated.
Therefore, the template parameter T of this get_deleter overload cannot be deduced; therefore this function template isn't instantiated, and shared_array<Derived> isn't instantiated.
Deduction failure is a special kind of failure (like SFINAE): It doesn't lead to the program being ill-formed (i.e. it doesn't lead to a compilation error). Rather, the function template for which deduction didn't succeed simply doesn't add a function to the overload set. If there are other functions in the overload set, one of those can be called instead.
Another function template
template<class D, class T> D * get_deleter( shared_ptr<T> const & p )
from boost/smart_ptr/shared_ptr.hpp runs through the same process. However, deduction succeeds here, and a specialization get_deleter<H, T> (with the H and T from to_std_ptr) is added to the overload set. It'll later be chosen by overload resolution.
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;
};