Thanks,
- Marsh
struct sample
{
float member_one;
};
template <typename T, int N> struct choose_p2mem { };
template <> struct choose_p2mem<sample, 1>
{
// Error: a member of type "int sample::*const" cannot have an in-
class initializer
//static int sample::* const ptr2mem = &sample::member_one;
static float sample::* const ptr2mem;
};
// Error: "float sample::*const choose_p2mem<sample, 1>::ptr2mem" is
not an entity that can be explicitly specialized
template <>
float sample::* const choose_p2mem<sample, 1>::ptr2mem =
&sample::member_one;
void f()
{
BOOST_STATIC_ASSERT(&sample::member_one == choose_p2mem<sample,
1>::ptr2mem);
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
It's nothing to do with the ptr-to-member; the same thing would happen
with any type. Try the following:
float sample::* const choose_p2mem<sample, 1>::ptr2mem =
&sample::member_one;
(without the "template <>").
The point is that "choose_p2mem<sample, 1>" is not a template; it's a
full specialization, which is just a plain class. Note that this
definition will need to go in a cpp file, not a header file, like all
class member definitions.
Yechezkel Mett
Thanks, that was part of my compile error, but I'm not much closer to
my goal. It seems I can provide types and integral constant
expressions
as compile-time constants from my template, but not pointer-to-
members.
#include <boost/mpl/bool.hpp>
#include <boost/mpl/assert.hpp>
struct sample { float member_one; };
template <
typename T,
int N,
float sample::* P2M // can take a pointer-to-member just fine
>
struct republish_my_own_template_args_as_compile_time_const
{
typedef T t; // OK
static const int n = N; // OK
// static float sample::* const p2m = P2M; // No luck.
// "a member of type "float sample::*const" cannot have an in-class
initializer"
};
So I made a half-successful attempt to trick the compiler into
allowing it:
// Simple metapredicate for testing. Note that we can specialize
on
// a pointer-to-member just fine.
template <float sample::* P2M>
struct is_sample_member_one : boost::mpl::false_ { };
template <> struct is_sample_member_one<&sample::member_one> :
boost::mpl::true_ { };
BOOST_MPL_ASSERT((is_sample_member_one<&sample::member_one>)); //
OK
BOOST_MPL_ASSERT_NOT((is_sample_member_one<0>)); // OK
// Template to specialize and yield pointer-to-member.
template <typename T, int N, typename V = void> struct
choose_p2mem;
// This actually compiles. It's not a full specialization.
template <typename V> struct choose_p2mem<sample, 1, V>
{
static float sample::* const ptr2mem;
};
template <typename V>
float sample::* const choose_p2mem<sample, 1, V>::ptr2mem =
&sample::member_one;
int main()
{
// Works at run-time.
if (choose_p2mem<sample, 1>::ptr2mem != &sample::member_one)
throw -1;
// Breaks at compile-time.
//BOOST_MPL_ASSERT((is_sample_member_one<choose_p2mem<sample,
1>::ptr2mem>));
return 0;
}
But (as usual) the compiler gets the last laugh. I can make it produce
the correct pointer-to-member at run-time, but it refuses to use its
knowledge at compile-time.
Any other ideas?
So it seems I can take ptr-to-mems as non type template arguments,
and even specialize on them, but cannot publish them from structs
as compile-time constants. I don't see an obvious reason for this
limitation. Perhaps it's just one of those little corners below
the horizon of the vendors and standards group.
Will I be able to do this in C++ 0x?
- Marsh
What about something like:
struct A { float f; };
template<float A::* P2M>
struct B
{
static float A::* const p2m;
};
template<float A::* P2M> float A::* const B<P2M>::p2m = P2M;
template<float A::* P2M>
struct C
{
};
int main()
{
A a;
a.f = 4.2;
//prints 4.2
std::cout << a.*B<&A::f>::p2m << std::endl;
//used as compile-time constant
C<B<&A::f>::p2m> c;
}
or am I misunderstanding the issue?
Chris
I would have expected some use of constexpr to allow this, but looking
at the draft C++0x standard (N2588) I see the following:
"""
14.3.2 Template non-type arguments [temp.arg.nontype]
1 A template-argument for a non-type, non-template template-parameter
shall be one of:
� an integral constant expression; or
� the name of a non-type template-parameter; or
� the address of an object or function with external linkage,
including function templates and function template-ids but excluding
non-static class members, expressed as & id-expression where the & is
optional if the name refers to a function or array, or if the
corresponding template-parameter is a reference; or
� a constant expression that evaluates to a null pointer value (4.10);
or
� a constant expression that evaluates to a null member pointer value
(4.11); or
� a pointer to member expressed as described in 5.3.1.
"""
Note that the fourth and fifth items allow constant expressions for
pointers and pointer-to-members only if they evaluate to null. The
third and sixth items allow pointers and pointers to members if they
are of the form &object or &class::member, but no other expressions.
So it seems that there is no way to do it.
> Any other ideas?
Depending on what you are trying to do you might be able to invert the
logic. For your original example, you can do something like this:
struct sample
{
float member_one;
};
template<class T, int N, template<float T::*> class F>
struct choose_and_apply_p2m;
template<template<float sample::*> class F>
struct choose_and_apply_p2m<sample, 1, F>
{
typedef F<&sample::member_one> type;
};
template<float sample::* p>
struct is_member_one
{
static const bool value = false;
};
template<>
struct is_member_one<&sample::member_one>
{
static const bool value = true;
};
void f()
{
BOOST_STATIC_ASSERT((choose_and_apply_p2m<sample, 1,
is_member_one>::type::value));
}
Yechezkel Mett