Re: Why are template aliases not truly aliases?
{ Double spacing manually removed; please avoid double spacing. -mod }
On Saturday, November 3, 2012 6:30:05 PM UTC-4, Dave Abrahams wrote:
Yeah, that is unfortunate. But consider this:
template <class A, class B> struct Binary;
template <class A> struct Unary = X<A,A>;
template <template <class A, class B> class Two>
struct Binary2;
template <template <class A> class One>
struct Unary2;
// Which of these lines should compile?
typedef Binary2<Binary> W;
typedef Binary2<Unary> X;
typedef Unary2<Binary> Y;
typedef Unary2<Unary> Z;
Just W and Z, I wouldn't expect the others to compile. Looking back
at my post, I realize the confusion and I should have been more
specific. I clarified a bit better in my post to the boost mailing
list and I'll repeat it a little here. What I expect is that when a
template alias "A" and its underlying template (or other template
alias or type) "T" have the same template parameter list; where the
template parameters (if any) of the underlying entity being aliased
are /deducible/ from the alias's template parameters; and where the
correspondence between the parameters of "A" and the underlying
entity are the same between "A" and "T" , then they should be
considered equivalent. This process would be applied recursively
for aliases of aliases.
For example:
////////////////////
// The underlying template used for aliasing
template< class, class > struct underlying_template;
// I'd expect this alias to be the same as underlying_template
// as in they would be considered to be the same when passed as
// a template argument.
template< class P0, class P1 >
using binary_alias_0 = underlying_template< P0, P1 >;
// The following would not be equivalent
template< class P0, class P1 >
using binary_alias_1 = underlying_template< P0, P0 >;
// I'd expect the following aliases to be the same as each other
// However, they are not the same as underlying_template.
template< class P > using unary_alias0 = underlying_template< P, P >;
template< class P > using unary_alias1 = underlying_template< P, P >;
// The following aliases would be considered the same, however
// they are not the same as underlying_template.
template< class P0, class P1 >
using r_alias0 = underlying_template< P1, P0 >;
template< class P0, class P1 >
using r_alias1 = underlying_template< P1, P0 >;
////////////////////
The type of situations where this "true" aliasing can potentially be
performed (when the parameters are deducible) is already exploited in
C++11 and can be seen when using a template alias as a function
template parameter:
////////////////////
#include <utility>
// A simple template
template< class T > struct foo {};
// An alias where the underlying template's
// parameters are deducible from the alias's
// template parameters and vice versa.
template< class T >
using foo_alias = foo< std::pair< T, T > >;
// A function template taking a foo< T >, deduction works
template< class T >
void bar( foo< std::pair< T, T > > );
// The equivalent function written in terms of the alias
template< class T >
void bar_alias( foo_alias< T > );
int main()
{
// All of these work in C++11, as expected
bar( foo< std::pair< int, int > >() );
bar( foo_alias< int >() );
bar_alias( foo< std::pair< int, int > >() );
bar_alias( foo_alias< int >() );
}
////////////////////
The reason why the expressions in "main" are valid in the above
code snippet is because of the deducibility of the template
parameters between foo and its alias. Note that only one "bar"
is instantiated and only one "bar_alias" is instantiated. I argue
that in cases similar to these, where all parameters are deducible,
the compiler should be able to do more sophisticated aliasing in the
manner that one (or at least I) would intuitively expect. Detecting
if two template aliases are equivalent is a matter of pattern
matching in the case where each of the template parameters is
deducible.
I think the template alias is really a parameterized alias for the
types it generates, not an alias for the template used to generate
those types (if any), if you can parse that.
I agree to an extent, but again, the fact that template aliases work
with deduction in the manner described above tells me that the
relationship between an alias and the entity that it aliases is a bit
more sophisticated than that. I just think that it's not a stretch to
expect an equivalence relationship in the places where one may
clearly be made, though admittedly my use-case might be somewhat
unique.
So, if you want to find out if two templates, where one or both might
have the simple alias form you're using in your example, are "the
same," you should probably do
template <template <class A> class L, template <class A> class R>
: is_same<L<int>, R<int> >
{};
Make sense? Obviously this only works if you stick to the simple
alias form you're using.
Yes, I've considered passing dummy arguments, but until I have
archetypes working, I can't automatically determine exactly what
arguments would be valid (I.E. if one template parameter depends on
an earlier template parameter).
Anyway, I think this won't matter too much in the end for me, it just
means that I can't do full compatibility checking with respect to
concepts that have associated class templates, and I'll have to have
to do some compatibility checking in places that wouldn't normally
require it.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]