I am making statistical data managing software and have met with some gcc specific problems. So with huge effort I have managed to recreate problem in minimal example:
// generic statistic with complicated managing logic and single calculation
// funciton. there could be multiple classes like this
template<class T>
class statistic {
public:
using value_type = T;
virtual value_type f(int param) = 0;
};
// macro for defining a lot of statistics.
// it could take the name for different statistic class
#define STAT(NAME, TYPE) \
class NAME##_impl: public statistic<TYPE> { \
public: \
using typename statistic<TYPE>::value_type; \
\
value_type f(int param) override; \
} NAME; \
template<class T>
class vec {
public:
using value_type = T;
// (error) trying to make statistic follow the vector type
STAT(mult_by_2, value_type);
// this statistic should be int not regarding the vec type
STAT(integer_statistic, int);
value_type vec_data;
};
template<class T>
typename vec<T>::mult_by_2_impl::value_type vec<T>::mult_by_2_impl::f(int param) {
// value type in statistic<T> that was brought in current class scope
value_type var = 2*param;
return var;
}
template<class T>
typename vec<T>::integer_statistic_impl::value_type vec<T>::integer_statistic_impl::f(int param) {
return 1;
}
int main(int argc, char** argv) {
vec<double> v;
return 0;
}
This code complies nicely with clang++ using.cpp -std=c++17
, but g++ using.cpp -std=c++17
complains:
using.cpp:15:49: error: declaration of ‘using statistic<T>::value_type’ changes meaning of ‘value_type’ [-Wchanges-meaning]
15 | using typename statistic<TYPE>::value_type; \
| ^~~~~~~~~~
using.cpp:26:9: note: in expansion of macro ‘STAT’
26 | STAT(mult_by_2, value_type);
| ^~~~
using.cpp:26:25: note: used here to mean ‘using vec<T>::value_type = T’
26 | STAT(mult_by_2, value_type);
| ^~~~~~~~~~
using.cpp:15:42: note: in definition of macro ‘STAT’
15 | using typename statistic<TYPE>::value_type; \
| ^~~~
using.cpp:23:15: note: declared here
23 | using value_type = T;
| ^~~~~~~~~~
As I have understood, the STAT(mult_by_2, value_type)
expands to class definition with the using typename statistic<value_type>::value_type
in it. But the first value_type
is from vec
and second value_type
is from the statistic</*first value_type*/>
. It is pretty confusing, I know.
The question is, How to make a statistic
inside vec
that will be of the value_type
type?
For code reuse (implementations of abstract base classes) you can use CRTP/mixin (which will avoid diamond inheritance in larger code bases). E.g. like this :
#include <iostream>
#include <typeinfo>
// your interfaces (abstract baseclasses)
template<class T>
class statistic
{
public:
virtual T f(int param) = 0;
};
template<class T>
class statistic2
{
public:
virtual T g(int param) = 0;
};
// Reusable inmplementations for your interfaces
template<typename Derived, typename T>
class statistic_impl : public statistic<T>
{
public:
T f(T param) override
{
std::cout << typeid(T).name() << " statistic_impl<" << typeid(Derived).name() << ">::f(int)\n";
return param;
}
};
template<typename Derived, typename T>
class statistic_impl2 : public statistic2<T>
{
public:
T g(T param) override
{
std::cout << typeid(T).name() << " statistic_impl2<" << typeid(Derived).name() << ">::f(int)\n";
return 2 * param;
}
};
// And then your vec class
// deriving from its CRTP/mixin implementations.
template<class T>
class vec :
public statistic_impl<vec<T>, T>,
public statistic_impl2<vec<T>, T>
{
};
int main()
{
vec<int> v;
v.f(1);
v.g(2);
return 0;
}