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
Related
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
I am using the CRTP and the base class has a template function. How can I use that member function in a templated derived class?
template <typename T>
struct A {
int f();
template <typename S>
int g();
};
struct B: public A<B> {
int h() { return f() + g<void>(); } // ok
};
template <typename T>
struct C: public A<C<T>> {
// must 'use' to get without qualifying with this->
using A<C<T>>::f; // ok
using A<C<T>>::g; // nope
int h() { return f() + g<void>(); } // doesn't work
};
* Edit *
An earlier question, Using declaration for type-dependent template name, including the comments, suggests this isn't possible and might be an oversight in the standard.
I don't know how to solve the problem with using statement (it should look something like using A<C<T>>::template g;, but this code does not compile with my compiler). But you may call g<void> method in one of the following ways:
this->template g<void>()
A<C<T>>::template g<void>()
See the answers to this question for details about the dark side of using template keyword.
Given the following two structs, one could derive from both nested 'Nested' classes, and call foo() and bar() from the derived object:
struct WithNested1 {
template<class T> struct Nested {
void foo();
};
};
struct WithNested2 {
template<class T> struct Nested {
void bar();
};
};
struct Test : WithNested1::Nested<Test>,
WithNested2::Nested<Test>
{
};
Test test;
test.foo();
test.bar();
But, if both of the outer classes were passed as variadic template arguments, how would you derive from them?
For example, this fails to compile:
template<typename... Ts>
struct Test : Ts::template Nested<Test>...
{
};
Test<WithNested1, WithNested2> test;
test.foo();
test.bar();
error: 'foo' : is not a member of 'Test'
error: 'bar' : is not a member of 'Test'
strangely, it compiles if the calls to foo() and bar() are removed.
template <typename... Ts>
struct Test : Ts::template Nested<Test<Ts...>>...
{
};
This is the same answer as above but I figured I'd explain how it works. First in your example Test has no template param (which the compiler should warn you of), but which should we give it. The point of CRTP is to give the class you inherit from a template param that is the same as your type, that way it has access to your methods and members through the of the template param. Your type in this case is Test<Ts...> so that is what you have to pass it. As #aschepler already pointed out normally you could use Test by itself but it's not in scope until your already inside the class.
I think this is a cleaner way of doing what you want.
template <typename T>
struct A {
void bar (){
static_cast<T*>(this)->val = 3;
}
};
template <typename T>
struct B {
void foo (){
static_cast<T*>(this)->val = 90;
}
};
template <template<class> class ... Ts>
struct Test : Ts<Test<Ts...>>...
{
int val;
};
int main() {
Test<A,B> test;
test.foo();
test.bar();
return 0;
}
The "injected class name" Test which can be used as an abbreviation of Test<Ts...> is not in scope where you tried to use Nested<Test>, since the class scope does not begin until the { token.
Use
template<typename... Ts>
struct Test : public Ts::template Nested<Test<Ts...>>...
{
};
This works:
template<typename... Ts>
struct Test : Ts::template Nested<Test<Ts...>>...
// ^^^^^^^
{
};
9/2:
[...]. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name. [...]
14.6.1/1:
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injectedclass-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-typespecifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.
just now I had to dig through the website to find out why template class template member function was giving syntax errors:
template<class C> class F00 {
template<typename T> bar();
};
...
Foo<C> f;
f.bar<T>(); // syntax error here
I now realize that template brackets are treated as relational operators. To do what was intended the following bizarre syntax is needed, cf Templates: template function not playing well with class's template member function:
f.template bar<T>();
what other bizarre aspects and gotcha of C++/C++ templates you have encountered that were not something that you would consider to be common knowledge?
I got tripped up the first time I inherited a templated class from another templated class:
template<typename T>
class Base {
int a;
};
template<typename T>
class Derived : public Base<T> {
void func() {
a++; // error! 'a' has not been declared
}
};
The problem is that the compiler doesn't know if Base<T> is going to be the default template or a specialized one. A specialized version may not have int a as a member, so the compiler doesn't assume that it's available. But you can tell the compiler that it's going to be there with the using directive:
template<typename T>
class Derived : public Base<T> {
using Base<T>::a;
void func() {
a++; // OK!
}
};
Alternatively, you can make it explicit that you are using a member of T:
void func {
T::a++; // OK!
}
This one got me upset back then:
#include <vector>
using std::vector;
struct foo {
template<typename U>
void vector();
};
int main() {
foo f;
f.vector<int>(); // ambiguous!
}
The last line in main is ambiguous, because the compiler not only looks up vector within foo, but also as an unqualified name starting from within main. So it finds both std::vector and foo::vector. To fix this, you have to write
f.foo::vector<int>();
GCC does not care about that, and accepts the above code by doing the intuitive thing (calling the member), other compilers do better and warn like comeau:
"ComeauTest.c", line 13: warning: ambiguous class member reference -- function
template "foo::vector" (declared at line 8) used in preference to
class template "std::vector" (declared at line 163 of
"stl_vector.h")
f.vector<int>(); // ambiguous!
The star of questions about templates here on SO: the missing typename!
template <typename T>
class vector
{
public:
typedef T * iterator;
...
};
template <typename T>
void func()
{
vector<T>::iterator it; // this is not correct!
typename vector<T>::iterator it2; // this is correct.
}
The problem here is that vector<T>::iterator is a dependent name: it depends on a template parameter. As a consequence, the compiler does not know that iterator designates a type; we need to tell him with the typename keyword.
The same goes for template inner classes or template member/static functions: they must be disambiguated using the template keyword, as noted in the OP.
template <typename T>
void func()
{
T::staticTemplateFunc<int>(); // ambiguous
T::template staticTemplateFunc<int>(); // ok
T t;
t.memberTemplateFunc<int>(); // ambiguous
t.template memberTemplateFunc<int>(); // ok
}
Out of scope class member function definition:
template <typename T>
class List { // a namespace scope class template
public:
template <typename T2> // a member function template
List (List<T2> const&); // (constructor)
…
};
template <typename T>
template <typename T2>
List<T>::List (List<T2> const& b) // an out-of-class member function
{ // template definition
…
}
It appears to me that C++ does not allow member template specialization in any scope other than namespace and global scope (MS VSC++ Error C3412). But to me it makes sense to specialize a base class's primary member template in the derived class because that is what derived classes do - specialize things in the base class. For instance, consider the following example:
struct Base
{
template <class T>
struct Kind
{
typedef T type;
};
};
struct Derived : public Base
{
/* Not Allowed */
using Base::Kind;
template <>
struct Kind <float>
{
typedef double type;
};
};
int main(void)
{
Base::Kind<float>::type f; // float type desired
Derived::Kind<float>::type i; // double type desired but does not work.
}
My question is why isn't it allowed?
I get what you're trying to do, but you are not doing it right. Try this :
struct Base{};
struct Derived{};
// Original definition of Kind
// Will yield an error if Kind is not used properly
template<typename WhatToDo, typename T>
struct Kind
{
};
// definition of Kind for Base selector
template<typename T>
struct Kind<Base, T>
{
typedef T type;
};
// Here is the inheritance you wanted
template<typename T>
struct Kind<Derived, T> : Kind<Base, T>
{
};
// ... and the specialization for float
template<>
struct Kind<Derived, float>
{
typedef double type;
};
My question is why isn't it allowed?
From my copy of the draft it appears that the following puts the above restriction:
In
an explicit specialization declaration for a class template, a member of a class template or a class member
template, the name of the class that is explicitly specialized shall be a simple-template-id.
The workaround is to specialize the enclosing class.
I will "ignore" the standard specifications and try a logical argument:
If you have two classes:
class A
{
struct S { };
};
class B: public A
{
struct S { };
};
A::S and B::S are two different types. Extending the logic to the template specializations, when you try to specialize an inner class declared in base class through an inner class in derived class, you actually are trying to define a different type, with the same name (but another naming scope).