In my public header, I am trying to add a pointer to an object which is only defined privately. Normally that would be simple. I just forward-declare. However, the object is a bit complicated and I'm not sure how to forward declare it.
public.h:
#include <memory>
class StateMachine; // This forward declaration doesn't work
class MyClass {
std::unique_ptr<StateMachine> sm_;
public:
MyClass();
}
public.cpp
#include <public.h>
#include "statemachine.h" //StateMachine declared in full here
MyClass::MyClass
: obj_(std::make_unique<StateMachine>(*this))
{
}
statemachine.h:
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine.hpp>
class MyClass;
struct _StateMachine : public boost::msm::front::state_machine_def<_StateMachine>
{
MyClass parent_;
public:
_StateMachine(MyClass& p_parent);
};
typedef boost::msm::back::state_machine<_StateMachine> StateMachine;
where boost::msm::back::state_machine is defined as:
template <
class A0
, class A1 = parameter::void_
, class A2 = parameter::void_
, class A3 = parameter::void_
, class A4 = parameter::void_
>
class state_machine { ... };
Here are some of the forward-declarations I've tried and their error messages (g++)
class StateMachine;
error: conflicting declaration ‘typedef class boost::msm::back::state_machine<_StateMachine> StateMachine’
namespace boost{ namespace msm { namespace back { class state_machine; }}}
typedef boost::msm::back::state_machine StateMachine;
error: ‘boost::msm::back::state_machine’ is not a template
namespace boost{ namespace msm { namespace back { template <class T> class state_machine; }}}
class _StateMachine;
typedef boost::msm::state_machine<_StateMachine> StateMachine;
error: wrong number of template arguments (5, should be 1)
namespace boost{ namespace msm { namespace back { template<class T0, class T1, class T2, class T3, class T4> class state_machine; }}}
class _StateMachine;
typedef boost::msm::back::state_machine<_StateMachine> StateMachine;
error: error: wrong number of template arguments (1, should be 5)
namespace boost{ namespace msm { namespace back { template<class T0, class T1, class T2, class T3, class T4> class state_machine; }}}
class _StateMachine;
class Void;
typedef boost::msm::back::state_machine<_StateMachine, Void, Void, Void, Void> StateMachine;
error: conflicting declaration ‘typedef class boost::msm::back::state_machine<_StateMachine> StateMachine’
If you really don't want to include boost/msm/back/state_machine.hpp, then you need to forward-declare state_machine and use it correctly.
Your attempt fails because T1, T2, T3, and T4 are not given a type when instantiated.
namespace boost{ namespace msm { namespace back { template<class T0, class T1, class T2, class T3, class T4> class state_machine; }}}
class _StateMachine;
typedef boost::msm::back::state_machine<
_StateMachine
/* need types here, we don't know there are default ones defined later */
> StateMachine;
Your attempt with class Void; fails because Void is not parameter::void_, so the two typedefs are different.
Note before the solution: in C++, you should probably use using instead of typedef. Also, in C++17, you can do namespace boost::msm::back { ... }.
The solution is to forward declare parameter::void_ as well and use it in the alias definition.
namespace boost::msm::back {
namespace parameter {
// careful, this might be in a different namespace if there is a using namespace somewhere.
class void_;
}
template<class T0, class T1, class T2, class T3, class T4> class state_machine;
}
class _StateMachine;
using StateMachine = boost::msm::back::state_machine<
_StateMachine,
boost::msm::back::parameter::void_,
boost::msm::back::parameter::void_,
boost::msm::back::parameter::void_,
boost::msm::back::parameter::void_
>;
Demo
Unfortunately, you cannot provide T1 through T4 with default types because you cannot have multiple declarations of the default parts of template parameter lists, even if they match.
I don't see any other solution than this or including the boost header.
Related
I have the following class definition:
template <int B>
class SparseBitvector
{
typedef typename std::vector<std::bitset<B>> BitsetVector;
public:
class iterator: public std::iterator<...>
{
public:
explicit iterator(..., BitsetVector::iterator* _chunksIt, ...) {...}
}
}
At compilation I get this error:
/project/powerset/src/powerset/sparse_bitvector.h:96:125: error: 'std::BitsetVector<std::bitset<(long unsigned int)B>, std::allocator<std::bitset<(long unsigned int)B> > >::iterator' is not a type
explicit iterator(SortedStaticSet<EHRule> *_offsets, std::vector<std::bitset<B>> *_chunks, SetElement* _offssetsIt, BitsetVector::iterator* _chunksIt, int _indInBitset)
Is it allowed to use template value inside a nested class?
If not, is there some other error I am making?
You need to use typename keyword for depended types. Here for example
typename BitsetVector::iterator* _chunksIt
//^^^^^^
Secondly, there is no typename needed for the template type alias
typedef std::vector<std::bitset<B>> BitsetVector;
// or better use
// using BitsetVector = std::vector<std::bitset<B>>;
The minimal example for the above have considered as follows:
#include <vector>
#include <iterator>
#include <bitset>
template <int B>
class SparseBitvector
{
typedef std::vector<std::bitset<B>> BitsetVector;
public:
class iterator : public std::iterator<std::input_iterator_tag, BitsetVector>
{
public:
explicit iterator(std::input_iterator_tag, BitsetVector, typename BitsetVector::iterator* _chunksIt)
{
}
};
};
I have followed this post: Class template SFINAE
to instantiate the template class conditionally.
That works perfectly for the classes which have only one template parameters, as shown in the link above.
However, I have two (template)arguments, and I would like to do certain SFINE check.
Following is a minimal example of my code.
#include <type_traits>
#include <string>
template<class T, class U, class R> using arithmetic_types = std::enable_if_t<
std::is_arithmetic_v<T> &&
std::is_arithmetic_v<U>,
R
>;
template<class T, class U, class Enable = void> class MyClass;
template<class T, class U, arithmetic_types<T, U, void>>
class MyClass {
public:
MyClass() = default;
};
int main()
{
MyClass<int, int> o; // should work
MyClass<int, double> o1; // should work
MyClass<int, std::string> o2; // should be a complier error
return 0;
}
Above gave me the error message: https://godbolt.org/z/BEWJMp
error C3855: 'MyClass': template parameter 'Enable' is incompatible with the declaration
error C2079: 'o' uses undefined class 'MyClass'
error C2079: 'o1' uses undefined class 'MyClass'
error C2079: 'o2' uses undefined class 'MyClass'
Unfortunately, I could not understand the error message(error C3855:).
Why I can't do the same principle shown in the above link to more template parameters?
And what is the best solution for this?
The problem is in the template specialization of MyClass. The specialization should be parameterized only on the two classes T and U, the test should be put in the declaration, as in the example below.
#include <string>
#include <type_traits>
template <class T, class U, class R>
using arithmetic_types = std::enable_if_t<
std::is_arithmetic_v<T> && std::is_arithmetic_v<U>, R>;
template <class T, class U, class Enable = void>
class MyClass;
template <class T, class U> //<- Remove the test from here
class MyClass<T, U, arithmetic_types<T, U, void>> //<- Put the test here.
{
public:
MyClass() = default;
};
int main()
{
MyClass<int, int> o; // should work
MyClass<int, double> o1; // should work
MyClass<int, std::string> o2; // should be a complier error
return 0;
}
Demo: https://godbolt.org/z/xTnwo9
If the base class depends on the template parameter, its scope is not examined in the unqualified name lookup. I can use the using declaration to introduce names from the base class. Suppose now I have a type alias template in the base class. Can the using declaration be used to introduce it into the derived class?
template<class T>
struct Base
{
using Type1 = int;
template<typename S>
using Type2 = S;
};
template<class T>
struct Derived : Base<T>
{
using typename Base<T>::Type1; // Fine
//using Type1 = typename Base<T>::Type1; // Also fine
template<typename S>
using Type2 = typename Base<T>::template Type2<S>;
};
Can the line for Type2 be replaced with something similar to the (uncommented) line for Type1?
I quoted a template class to put it into a mpl::vector doing this:
boost::mpl::vector<int, boost::mpl::quote2<std::pair>>
Then, I obtained the 2nd element like this:
using A=typename boost::mpl::at<T, boost::mpl::int_<2>>::type;
I need now to pass the original template class to a class like this:
template<class A, template<class, class> class C>
class B{
C<A, B*> _c;
};
I'm tried using apply or bind, but couldn't find the way to make B accept the second parameter.
I get errors of the kind:
error: template argument for template template parameter must be a class template or type alias template
edit:
Example code:
#include <boost/mpl/vector.hpp>
#include <boost/mpl/quote.hpp>
#include <boost/mpl/at.hpp>
template<class, class> class A{};
template<class A, template<class, class> class C>
class B{
C<A, B*> _c;
};
using T=boost::mpl::vector<int, boost::mpl::quote2<A>> ;
using T1=typename boost::mpl::at<T, boost::mpl::int_<0>>::type;
using T2=typename boost::mpl::at<T, boost::mpl::int_<1>>::type;
int main(){
B<T1, T2> b;
return 0;
}
I get:
error: template argument for template template parameter must be a class template or type alias template B<T1, T2> b;
MPL is still largely a C++03 lib AFAIK and you're trying to make it generate something that didn't conceptually exist before C++11. I suspect that having quote work in that case is a coincidence of syntax rather than intended functionality.
The following code compiles successfully in VC2013:
#include <boost/mpl/vector.hpp>
#include <boost/mpl/quote.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/at.hpp>
template<class, class> class A{};
template<class A, template<class, class> class C>
class B{
C<A, B*> _c;
};
using T = boost::mpl::vector < int, boost::mpl::quote2<A> > ;
using T1 = boost::mpl::at<T, boost::mpl::int_<0>>::type;
using T2 = boost::mpl::at<T, boost::mpl::int_<1>>::type;
template<typename X1, typename X2>
using TT2 = typename boost::mpl::apply<T2, X1, X2>::type;
int main(int argc, char* argv[])
{
B<T1, TT2> b;
return 0;
}
I have a template class defined as follow :
template <class T1, class T2>
class MyClass { };
In this class, I need a struct that contains one member of type T1. How can I do that ?
I tried the following, but it didn't work :
template <class T1, class T2>
class MyClass {
typedef struct {
T1 templateMember;
// rest of members
} myStruct;
// rest of class definition
};
EDIT: As requested, I use VS2008 and get the following error :
'MyClass<T1,T2>::myStruct' uses undefined class 'T1'
Just remove typedef:
template <class T1, class T2>
class MyClass {
struct myStruct{
T1 templateMember;
// rest of members
} ;
};
template <class T1>
struct myStruct{
T1 templateMember;
// rest of members
};
template <class T1, class T2>
class MyClass {
myStruct<T1> mystruct;
// rest of class definition
};
Are you sure this is exactly what you typed?
template <class T1, class T2>
class MyClass {
public:
typedef struct {
T1 templateMember;
// rest of members
} myStruct;
// rest of class definition
};
MyClass<int, float> c;
MyClass<int, float>::myStruct ms;
This compiles and works just fine for me in VS2008 SP1. Note that I added the public: so that I could access myStruct, but it does not affect the correctness of the rest of the declaration.