member template specialization and its scope - c++

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).

Related

How to declare a member in a base template class where the type is dependent of the derived class?

Given a base class using CRTP, I'm looking at declaring a member in the base template class where the type is dependent of the derived class.
While the following works as intended:
template <class T> class BaseTraits;
template <class T> class Base {
using TypeId = typename BaseTraits<T>::TypeId;
TypeId id;
public:
Base() { id = 123; }
TypeId getId() { return id; }
};
class Derived;
template <> class BaseTraits<Derived> {
public:
using TypeId = int;
};
class Derived : public Base<Derived> {};
int main(int argc, char ** argv) {
Derived foo;
return foo.getId();
}
I wonder if I could simplify the implementation. I could add a second template parameter to the Base template, and make BaseTraits simpler or even get rid of it. However the above snippet is already an attempt to remove the second template parameter. I'm looking at solutions that doesn't involve a second template parameter for Base.
I've tried something like the following but it doesn't compile:
error: invalid use of incomplete type 'class Derived'
template <class T> class Base {
using TypeId = typename T::TypeId;
TypeId id;
public:
Base() { id = 123; }
TypeId getId() { return id; }
};
class Derived : public Base<Derived> {
public:
using TypeId = int;
};
int main(int argc, char ** argv) {
Derived foo;
return foo.getId();
}
UPDATE:
I'm limited to c++14.
Base must be a template.
Performance is a must.
Is it possible to make a member type directly dependent on the derived class? Taking appart the result type of a member function declared with auto (deduced return type), it is not possible.
So the use of a type-trait as you do in your solution is the best and only solution.
The reason is that a base class must be a complete type when the derived class is defined: the compiler must first instantiate and parse the base class definition before it parses the derived class definition, C++ standard N4140 [derived.class]/2 (bold is mine):
The type denoted by a base-type-specifier shall be a class type that is not an incompletely defined class;[...]
What about something like this:
template <typename T, typename TypeId> class Base
{
private:
TypeId id;
public:
Base() { id = 123; }
TypeId getId() {return id;}
};
class Derived : public Base<Derived, int> {};
This is kind of simplified, but you pay some price for it.
#include <any>
template <class T> class Base {
std::any id; // expensive, but cannot have T::TypeId here
public:
Base() : id(123) {}
auto getId() {
return std::any_cast<typename T::TypeId>(id);
} // T::TypeId is OK inside a member function
};
class Derived : public Base<Derived> {
public:
using TypeId = int;
};
Why not reversing the class hierarchy?
template <class T>
class Base : T {
using TypeId = typename T::TypeId;
TypeId id;
public:
Base() { id = 123; }
TypeId getId() { return id; }
};
struct BasicDerived {
using TypeId = int;
};
using Derived = Base<BasicDerived>;
Actually, I thought some more... this isn't too unpleasant:
You could have a binding struct, could even be written as a macro, declared just before the real class.
The binding struct defines the enum and an incomplete typedef to the real class.
The template is defined before all of that, but uses typename to defer its dependency, but it is instanced by the real class and only dependant on the binding struct
template <class ThatClassWrapper>
class MyBase
{
protected:
typedef typename ThatClassWrapper::TypeId TypeId;
typedef typename ThatClassWrapper::RealClass ThatClass;
TypeId typeIdValue;
TypeId GetTypeId() { return typeIdValue; }
std::vector<ThatClass*> storage;
};
class SomeClass;
namespace TypeIdBinding
{
struct SomeClass
{
enum TypeId
{
hello, world
};
typedef ::SomeClass RealClass;
};
}
class SomeClass: public MyBase<TypeIdBinding::SomeClass>
{
public:
bool CheckValue(TypeId id)
{ return id == typeIdValue; }
};
Note that the real class is using TypeId as defined in the template base, and the named members are not directly visible. You could fix that by having the template Base derive from the binding struct (confirmed that it compiles that way). though I actually like that in c++11 you can export or typedef just the enum typename from another namespace and use that type name as a prefix for the enum members, helping to avoid name pollution.
To be honest you have hit the wall of hard circular dependencies.
Any way out will be smelly.
Two template arguments seems like a small price in the end.
Could you declare a dummy template class that takes Derived and TypeID? I don't think it gains you anything, though.
Is TypeID:Derived a 1:1 mapping? Would it feel better to over-represent that 1:1 mapping with another helper template to back-look-up Derived from TypeID? Note that TypeID would need to be defined outside the Derived class to do this.
Does TypeID really need to be defined inside the class? Could it leach off the passed-in definition in Base to support the existing use of the internal typedef?
Can you double-include? Split or macriose your definition of derived so that typeid is in a base class definition that can be included before the template? This DerivedBase could be declared in a namespace and contain a typedef link back to the full Derived class so Base can find it for references.

c++ defining type of a member class without template argument

I am trying to set the type of the member of a class, without passing it through template argument.
In details:
// Forward declaration:
class A;
class B;
class Base
{
};
template <class T>
class Derived : public Base
{
private:
T2 var;
};
where T could be either class A or class B.
What I would like to do is for Derived<A> T2 is int (for instance) and for Derived<B> T2 is double (for instance). I would like to avoid the following solution:
template <class T1, class T2>
class Derived : public Base
{
private:
T2 var;
};
I want to avoid this because for Derived<A> there could be various possible combinations for T2: Derived<A,int>, Derived<A,double>, ...
What I want is that the type of T2 is unique for the entire Derived<A>.
Any idea how to solve that ?
Update: The comments show that the original problem you are trying to solve is not completely explained in the question. I'll leave the original answer nevertheless at the bottom of this answer.
You cannot have two Derived<A> with different types T2 for the var member. In addition, a variable defined by the User can not influence the type of the member variable. Variable values are set at runtime, types are determined at compiletime.
To store a type somehow defined by the user, you will have either have to restrict the variable to a set of known types or use one type that contains a serialized version of the variable's content. The set of known types is often used in the context of databases, where the fields can have one of several predefined types (e.g. String, Integer, Boolean, Double). The type for the member variable then could be a Boost.Variant, restricted to C++ representations of that type. Another application of "user defined types" are where the user of your program has to somehow define the layout and interpretation of the type and its object, for example if your program is the interpreter of some scripting language. In that case again a Boost.Variant (or something similar) can be of use, or, since the value is probably some user provided input, just store the serialized value in a string and interpret it every time you have to deal with it.
Original answer:
This is usually done via template metaprogramming, in this case a type function (sometimes, depending on the context, part of a traits or policy class):
template <class T>
struct DerivedMemVarType {
typedef double type; //default
};
template<>
struct DerivedMemVarType<A> {
typedef int type;
};
And then:
template <class T>
class Derived : public Base
{
typedef typename DerivedMemVarType<T>::type T2;
private:
T2 var;
};
You can also leave out the default, so that any instantiation of Derived for a type that you have not mapped in your function will give a compile error:
template <class T>
struct DerivedMemVarType; //default is not defined
template<>
struct DerivedMemVarType<A> {
typedef int type;
};
template<>
struct DerivedMemVarType<B> {
typedef double type;
};
//...
Derived<C> dc; // ...template error mess....
// --> error: invalid use of incomplete type 'struct DerivedMemVarType<C>'
if you do not have any type specific function call, you can use something like...
class A;
class B;
class Base
{
};
template <class T>
class Derived : public Base
{
public:
Derived(T startVal):var(startVal){}
private:
T var;
};
template <typename T>
Derived<T> MakeDerived(T initValue)
{
return Derived<T>(initValue);
}
and now you can use it the following and the compiler should know what type you are passing to the function.
int initialValue = 0;
auto derived = MakeDerived(initialValue);
I think you can create a separate class that just holds a typedef which you then specialize and use in your Derived class.
template<typename T>
class VarType {
public:
typedef int TheType;
}
template <>
class VarType<B> {
public:
typedef double TheType;
};
template <typename T>
class Derived : public Base {
private:
typename VarType<T>::TheType var;
};

How to derive from a nested class of a variadic template argument?

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 <>.

Specialization of a template to a nested class type

Is there anyway to specialize a trait template for a nested class? I've tried it in the three places noted below, each with the given error. I've seen questions regarding specializing nested template classes, but that's not what I'm trying to do here-- I'm trying to specialize a trait class that is used by the nested class.
The TraitUser class makes use of the definitions within the Trait as specialized to a specific type T. Perhaps most relevantly, it uses a trait member to initialize a base class.
template<T>
class TraitUser:public X<typename Trait<T>::Type>
{
//Trait<T> gets used in here
};
//class A;
//class A::B; <-incomplete type used in nested name
//template<>
//struct Trait<A::B>
//{};
class A
{
private:
//class B;
//template<> <-explicit specialization at class scope
//struct Trait<B>
//{};
class B:TraitUser<B>
{};
};
//template<> <- specialization after instantiation
//struct Trait<A::B>
//{};
It looks like the root of the trouble is not being able to forward declare a nested class and also not being able to define a specialization inside a class declaration.
I'm trying this under clang using C++11.
There's some complicated declaration ordering here:
template <class T>
struct Trait;
template <class T>
struct X
{};
template<class T>
class TraitUser:public X<typename Trait<T>::Type>
{
//Trait<T> gets used in here
};
class A
{
private:
class B;
};
template<>
struct Trait<A::B>
{
typedef int Type;
};
class A::B : public TraitUser<B>
{};

Add/Remove data members with template parameters?

Consider the following code :
template<bool AddMembers> class MyClass
{
public:
void myFunction();
template<class = typename std::enable_if<AddMembers>::type> void addedFunction();
protected:
double myVariable;
/* SOMETHING */ addedVariable;
};
In this code, the template parameter AddMembers allow to add a function to the class when it's true. To do that, we use an std::enable_if.
My question is : is the same possible (maybe with a trick) for data members variable ? (in a such way that MyClass<false> will have 1 data member (myVariable) and MyClass<true> will have 2 data members (myVariable and addedVariable) ?
A conditional base class may be used:
struct BaseWithVariable { int addedVariable; };
struct BaseWithoutVariable { };
template <bool AddMembers> class MyClass
: std::conditional<AddMembers, BaseWithVariable, BaseWithoutVariable>::type
{
// etc.
};
First off, your code just won't compile for MyClass<false>. The enable_if trait is useful for deduced arguments, not for class template arguments.
Second, here's how you could control members:
template <bool> struct Members { };
template <> struct Members<true> { int x; };
template <bool B> struct Foo : Members<B>
{
double y;
};