Compile Time Template restriction C++ - c++

Basically I have 4 classes:
OverVoid
Meta: that inherits OverVoid
Physical: which has nothing to do with the above
Move: a templated class
I want move's template to accept objects of only OverVoid type i.e. OverVoid and Meta.
class OverVoid{
public:
virtual ~OverVoid(){
};
};
class Meta: public OverVoid{
};
class Physical{
public:
};
template<typename _Ty>
class Move{
};
I want an error to be trown at compile time,
I know there is a way with boost but I cannot use Boost (dev issues with my company)
any ideas?

The simplest thing is just a static_assert:
template<typename _Ty>
class Move {
static_assert(std::is_base_of<OverVoid, _Ty>::value,
"_Ty must inherit from OverVoid.");
};
Note that this allows OverVoid to be a private or inaccessible base. If you want to require that it's a public base, you could instead require:
static_assert(std::is_convertible<_Ty*, OverVoid*>::value,
"_Ty must inherit publicly from OverVoid.");

You can hide the template definition for classes not of type OverVoid
template<typename _Ty,
class = typename std::enable_if<std::is_base_of<OverVoid, _Ty>::value>::type>
class Move{
};
You then get an error when attempting to compile a class of non-OverVoid type.
int main() {
Move<Meta> a;
Move<OverVoid> b;
Move<Physical> c;
// your code goes here
return 0;
}
Error:
prog.cpp: In function 'int main()':
prog.cpp:29:15: error: no type named 'type' in 'struct std::enable_if<false, void>'
Move<Physical> c;

Use std::enable_if:
template <typename T>
struct is_overvoid_or_meta
{
static const bool value = false;
};
template <> struct is_overvoid_or_meta<OverVoid>
{
static const bool value = true;
};
template <> struct is_overvoid_or_meta<Meta>
{
static const bool value = true;
};
//Add specialization for all valid types - this allows you to more precisely tell, what types can be used as a template argument for Move
And then:
template<typename _Ty>
class Move
{
typedef std::enable_if<is_overvoid_or_meta<_Ty>::value, _Ty>::type Type;
};
You will get compile-time error for every type, that is not OverVoid or Meta (or, more general, for every T, for which is_overvoid_or_meta<T>::value is false - if you will add more of them in the future, you may want to chnge is_overvoid_or_meta to more general, like is_acceptable_by_move or something):
int main()
{
Move<OverVoid> m1;
Move<Meta> m2;
Move<int> m3;
return 0;
}
Output:
error: no type named 'type' in 'struct std::enable_if'
typedef typename std::enable_if::value, _Ty>::type Type;
Live sample.
This is very nice solution, because it cannot be tricked - additional template parameter for Move can always be specified manually (unless OverVoid and Meta are not exposed to client).

Related

C++ cannot use template template class with class member

I am possibly misunderstanding the template template classes and their use in C++. With the following declaration:
template <typename Parameter>
class A { Parameter p; };
template <template <typename> typename Class, typename Parameter>
class B { Class<Parameter> q; };
B<A, int> b;
b.q.p = 0;
I can declare a variable b, but I cannot access class members p nor q. That line yields these two errors:
error: unknown type name 'b'
b.q.p = 0;
^
error: cannot use dot operator on a type
b.q.p = 0;
^
Why is b not a variable, is it still a template? What is happening?
That code is almost legal, but classes have a default access specifier of private, so you need to mark them as public. This code works:
template <typename Parameter>
class A
{
public:
Parameter p;
};
template <template <typename> typename Class, typename Parameter>
class B
{
public:
Class<Parameter> q;
};
int main()
{
B<A, int> b;
b.q.p = 0;
}
The code you have written is correct and valid -- up until the assignment.
The reason that b.q.p = 0; fails is because class definitions default to private accessibility for all member definitions, so:
template <typename Parameter>
class A { Parameter p; };
template <template <typename> typename Class, typename Parameter>
class B { Class<Parameter> q; };
defines both p and q as private members, not public; this causes b.q to fail when trying to access q (the same would occur with q.p).
Either make these members public, or make class a struct (which defaults to public) and this will work:
template <typename Parameter>
struct A { Parameter p; };
template <template <typename> typename Class, typename Parameter>
struct B { Class<Parameter> q; };
Live Example
The error is because statements are not allowed outside of functions, unless that statement is a declaration. The error messages arise from the compiler trying to parse the code as a declaration. This is nothing to do with templates; you would see the same error with the simpler code:
struct { int x; } y;
y.x = 0;
The attempted assignment can occur inside a function, e.g.:
int main()
{
b.q.p = 0;
}
which will now give an error message about access control, that you can fix by making the members public.

Template class with template constructor specialization for initializing a templated base class

I have a template base class with a template parameter of type bool. The constructor parameter list of this base class depends on whether the template parameter is true or false. I want to derive from this class another template class with a template parameter that is any type. I need this derived class to call the correct constructor of that Base class depending on traits of that type.
The example below is not all-encompassing. Integral or not, the base class template bool could be for any type trait. Also, the type passed to the template parameter of the derived class could be any type.
I've tried several different methods for trying to syntax this thing out, but the closest I've gotten is with the code below. However it errors due to the fact that the derived class needs to be fully specialized.
#include <type_traits>
using namespace std;
template<bool IsIntegral> struct Base
{
template<bool IsI = IsIntegral,
typename I = typename enable_if<IsI>::type>
Base(int param) {}
template<bool IsI = IsIntegral,
typename I = typename enable_if<!IsI>::type>
Base() {}
};
template<class T> class CL :
Base<is_integral<T>::value>
{
public:
template<bool U = is_integral<T>::value> CL();
};
template<>
template<class T>
CL<T>::CL<true>() : // error: ISO C++ forbids declaration of ‘CL’ with no type [-fpermissive]
// CL<T>::CL<true>() :
// ^
// error: non-type partial specialization ‘CL<true>’ is not allowed
// error: no declaration matches ‘int CL<T>::CL()’
// CL<T>::CL<true>() :
// ^~~~~
Base(4) { }
template<>
template<class T>
CL<T>::CL<false>() // error: ISO C++ forbids declaration of ‘CL’ with no type [-fpermissive]
// CL<T>::CL<true>() :
// ^
// error: non-type partial specialization ‘CL<true>’ is not allowed
// error: no declaration matches ‘int CL<T>::CL()’
// CL<T>::CL<true>() :
// ^~~~~
{ }
int main () {
// Base<true> a; // won't compile as expected
Base<true> a(4);
// Base<false> b(4); // won't compile as expected
Base<false> b;
CL<int> c; // integral
CL<int*> d; // non-integral
// should work for any types other than int and int*
return 0;
}
I need to keep the type T generic and can't fully specialize the class CL. What is the correct syntax for this? Is there a workaround?
I'm using gcc-g++ version 8.3.0-6.
Thanks in advance!
After playing around with this, a little bit, I came up with this, which I guess falls under the category of "workaround", which you are willing to consider:
template<class T> class CL :
Base<is_integral<T>::value>
{
public:
CL() : CL{ is_integral<T>{} } {}
template<typename U=T>
CL(std::true_type) : Base<true>{4} {}
template<typename U=T>
CL(std::false_type)
{
}
};
Your derived class should be:
template<class T> class CL :
Base<is_integral<T>::value>{
public:
using base_t = Base<is_integral<T>::value>;
using base_t::base_t;
};
The using base_t::base_t; makes you inherit the constructors from your base class (available since c++11).
In your example you should also change:
CL<int> c; // integral
into, e.g.:
CL<int> c(10); // integral
C++20 and requires clauses
Prior to C++20, I would recommend tag dispatch to private delegating constructors as shown in #SamVarshavchik's answer.
Once you may use C++20, you can easily construct these kind of relations using a requires clause:
#include <type_traits>
template <bool IsIntegral>
struct Base {
Base(int param) requires IsIntegral {}
Base() requires (!IsIntegral) {}
};
template<typename T>
class CL : Base<std::is_integral<T>::value> {
static constexpr bool kIsIntegral = std::is_integral_v<T>;
public:
CL() requires kIsIntegral : CL::Base(4) {}
CL() requires (!kIsIntegral) : CL::Base() {}
};

No type named 'type' in CTRP derived class

I've been experimenting with the Curiously Recurring Template Pattern for a generic single-argument functor and have two implementations: one using a template template parameter which works and a second where I try to access the derived Functor::type in the interface class. In the latter example, the compiler (gcc 5.4.0) reports
error: no type named 'type' in 'struct Cube< double >'
template<class T, template<class> class Functor>
class FunctorInterface_1 {
private:
const Functor<T> &f_cref;
public:
FunctorInterface_1() : f_cref(static_cast<const Functor<T>&>(*this)) {}
T operator() ( T val ) const { return f_cref(val); }
}; // FunctorInterface_1 (works)
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
using Ftype = typename Functor::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
I then try to compile with T=double in main() of the following two classes:
template<class T>
struct Square : public FunctorInterface_1<T,Square> {
T operator()( T val ) const { return val*val; }
}; // Square
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
Can the FunctorInterface_2/Cube example be modified to work, or
is it necessary for the interface class to be templated on T as
in the first example? Thanks!
EDIT: Using gcc -std=c++14, I can get the second example to compile and run
by using auto return and argument types in FunctorInterface_1::operator(), however, as I understand, auto argument types are not part of the C++14 standard.
EDIT 2: Well I feel a bit thick. I just realized that I could template FunctorInterface_1::operator() on a new parameter, however, for the application I have in mind, I would really like my base class to be able to access types defined in the derived class.
When the line
using Ftype = typename Functor::type;
is processed in the base class, the definition of Functor is not available. Hence, you can't use Functor::type.
One way to get around this limitation is to define a traits class.
// Declare a traits class.
template <typename T> struct FunctorTraits;
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
// Use the traits class to define Ftype
using Ftype = typename FunctorTraits<Functor>::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
// Forward declare Cube to specialize FunctorTraits
template<class T> struct Cube;
// Specialize FunctorTraits for Cube
template <typename T> struct FunctorTraits<Cube<T>>
{
using type = T;
};
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
Working code: https://ideone.com/C1L4YW
Your code could be simplified to
template<typename TDerived> class
Base
{
using Ftype = typename TDerived::type;
};
template<typename T> class
Derived: public Base<Derived<T>>
{
using type = T;
};
Derived<int> wat;
It does not work because at the point of Base instantiation Derived class is not complete, and compiler is not aware of Derived::type existence yet.
You have to understand that when you instantiate Cube<T> FunctionInterface_2<Cube<T>> gets instantiated first. This means that Cube<T> is an incomplete type while this is happening.
So when the compiler gets to the line using Ftype = typename Functor::type; Functor is incomplete and you cannot access any of its nested types.
In your case you can change FunctionInterface_2 to:
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
template <class TT>
auto operator() ( TT && val ) -> decltype(f_cref(val)) const { return f_cref(val); }
};
So now accessing information about Functor is delayed until you call the operator() from FunctionInterface_2 at which point FunctionInterface_2 and Cube are fully instantiated.
Note: This question has already been answered by #r-sahu, but I'd like to elaborate on this and address clang's output specifically.
The problem can be demonstrated on a much smaller code sample: (#vtt suggested something similar)
template <typename _CRTP>
struct A {
using _C = typename _CRTP::C;
};
struct B : public A<B> {
using C = int;
};
Compiling that with clang will result in completely misleading error message: (godbolt)
<source>:3:32: error: no type named 'C' in 'B'
using _C = typename _CRTP::C;
~~~~~~~~~~~~~~~~^
<source>:6:19: note: in instantiation of template class 'A<B>' requested here
struct B : public A<B> {
^
1 error generated.
GCC's error message is a little more helpful: (godbolt)
<source>: In instantiation of 'struct A<B>':
<source>:6:19: required from here
<source>:3:33: error: invalid use of incomplete type 'struct B'
3 | using _C = typename _CRTP::C;
| ^
<source>:6:8: note: forward declaration of 'struct B'
6 | struct B : public A<B> {
| ^
As suggested in the accepted answer, implementing a trait type fixes the issue:
// this declaration must appear before the definition of A
template <typename _A>
struct a_traits;
template <typename _CRTP>
struct A {
// `a_traits<_CRTP>::type` is an incomplete type at this point,
// but that doesn't matter since `A` is also incomplete
using _C = typename a_traits<_CRTP>::type;
};
// this specialization must appear before the definition of B
template <>
struct a_traits<struct B> { // adding the type specifier `struct` will declare B
using type = int;
};
// specifying the template parameter will complete the type `A<B>`, which works since
// `a_traits<B>` is already complete at this point
struct B : public A<B> {
using C = int;
};

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;
};

member template specialization and its scope

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