I give up, please help explain this behaviour. The example I present below is the simplest one I could think of, but it sums up the problem (using g++ 4.9.2 on Cygwin with c++14 enabled). I want to create a class which will behave similar to std::mem_fn
. Here is my class:
template <class R, class T, R(T::*P)() const >
struct property {
static R get(const T& t) {
return (t.*P)();
}
};
where R
is the return type and T
is the type of the object I am interesting in. The third template parameter is a pointer to member function. So far, so good.
I then create a simple class which holds an integer as follows
class data_class {
public:
unsigned get_data() const {
return m_data;
}
private:
unsigned m_data;
};
This is the class which will be used in the property
class shown before.
Now I create two classes which inherit from data_class
as follows
struct my_classA
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
//same as my_classA, only templated
template <int I>
struct my_classB
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
They have the exact same inner typedef, but my_classB
is templated. Now the following types should in theory be the same:
using target_t = property<unsigned, data_class, &data_class::get_data>;
using test1_t = typename my_classA::data;
using test2_t = typename my_classB<1>::data;
However my compiler says that only test1_t
and target_t
are the same. The type deduced for test2_t
is apparently
property<unsigned int, data_class, (& data_class::get_data)> >
where this type has these brackets around the pointer to member function. Why test2_t
is not the same as target_t
? Here is the full code in case you want to try it on your system. Any help is much appreciated.
#include <type_traits>
class data_class {
public:
unsigned get_data() const {
return m_data;
}
private:
unsigned m_data;
};
//takes return type, class type, and a pointer to member function
//the get function takes an object as argument and uses the above pointer to call the member function
template <class R, class T, R(T::*P)() const >
struct property {
static R get(const T& t) {
return (t.*P)();
}
};
struct my_classA
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
//same as my_classA, only templated
template <int I>
struct my_classB
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
//used to produce informative errors
template <class T>
struct what_is;
//all 3 types below should, in theory, be the same
//but g++ says that test2_t is different
using target_t = property<unsigned, data_class, &data_class::get_data>;
using test1_t = typename my_classA::data;
using test2_t = typename my_classB<1>::data;
static_assert(std::is_same<target_t, test1_t>::value, ""); //this passes
static_assert(std::is_same<target_t, test2_t>::value, ""); //this does not
int main() {
what_is<test1_t> t1;
what_is<test2_t> t2;
}
I ran your code with c++11 because I'm not very familiar with c++14 yet. But all I replaced were the using (aliases) with typedefs and simplified the code a little bit. Nothing to affect its output.
I got the desired results by adding a typename T to the inherited classB template which when instantiated, it will replace the R with T, so in this case "unsigned".
#include <iostream>
#include <type_traits>
template <typename R, typename T, R(T::*P)() const>
struct property
{
static R get(const T& t)
{
return (t.*P)();
}
};
struct data_class
{
private:
unsigned m_data;
public:
unsigned get_data() const
{
return m_data;
}
};
struct my_classA : public data_class
{
typedef property<unsigned, data_class, &data_class::get_data> data;
};
template <typename T, int>
struct my_classB : public data_class
{
typedef property<T, data_class, &data_class::get_data> data;
};
int main()
{
typedef typename my_classA::data normClassA;
typedef typename my_classB<unsigned,1>::data tmplClassB;
std::cout<< std::is_same< property<unsigned, data_class, &data_class::get_data> , normClassA >::value <<std::endl;
std::cout<< std::is_same< property<unsigned, data_class, &data_class::get_data> , tmplClassB >::value <<std::endl;
}
The result is this:
~$g++ -std=c++11 test.cpp
~$./a.out
1
1
I think the problem has to do with the class template instantiation criteria because when I originally tried to print the sizeof's of the two classes, my_classA::data returned 1, but my_classB<1>::data ended in a compiller error. I'm still quite fuzzy as to why this occurs. Technically it should have instantiated the class template just fine. Maybe it's the property inside the classB template that was falsely instantiated. I'll look more into this but if you find the answer, please post it. It's an interesting one!
EDIT: The original code works fine on Cygwin GCC 4.8.2. Result is 1 and 1. Maybe it's just a gcc4.9.2 compiler issue.