Topic: [Concepts] Associated member functions


Author: Sean Hunt <rideau3@gmail.com>
Date: Mon, 10 Dec 2007 13:24:36 CST
Raw View
Given my understanding of the description of n2421, there are only
three good cases whereby you should actually use an associated member
definition. In all other cases, there is a superior, more compatible
alternative (I'll get to that in a moment). Each of these cases is
when there is absolutely no alternative - constructors, destructors,
and assignment operators. This is because a concept map is forbidden
from defining or declaring an associated member function. Rather than
declaring the following:

concept C <typename T>
{
    void T::foo();
}

You should declare:

concept C <typename T>
{
    void foo (T& t) { late_check { t.foo(); } }
}

(The use of the late_check is due to my understanding of
[ concept.fct]
- the default implementation is a constrained template and so must use
late_check to avoid the use of the archetype T', which doesn't include
a member function foo. If this is incorrect, I would appreciate being
corrected.)

The latter version is identical to the first from the user's
perspective. He can map the concept (or not, if it's auto) the same.
But if he needs to change the implementation - crucial if he doesn't
have access to the class in question - he can do so by simply
modifying the function definition in the concept map. The template
implementer just needs to rearrange the syntax, which is the heart of
my disagreement.

See, the problem is that this syntax represents a major step backward.
Now, rather than using the object-oriented "." operator, one has to
use the old, C-style method of passing the object itself. (there are
differences like the use of references, but that's not the important
point). Proper OO is one of the major attractions of C++ - it doesn't
make sense to make that mutually exclusive with constrained
templates.
If you try to use the first of my coding examples, allowing you to
have a constrained template that interfaces in an object-oriented
manner, then you sacrifice a third of C++'s (and C's) major axioms
(no
pun intended) - the avoidance of naming requirements for users.

My personal favored solution would be to allow a concept map (or
concept default implementation) to define a member function for the
archetype for any member function other than an assignment operator,
constructor, or destructor. Special rules for definition of other
functions within maps that must otherwise be members exist, clearly to
protect those three functions. Wouldn't it just be better to actually
protect them from instantiation? The only other reason I can think of
is to prevent giving a builtin type a member function, but since
concepts already abstract that, I don't see how that could be a
problem (barring stupid uses of late_check).

Alternatively, it could be changed so that in the archetype T' of T,
for each associated non-member function whose first parameter is a
reference to T, T' has a member function of the same name, with the
same reference qualifier as the associated function's first parameter,
and with parameters of the same types as the other parameters, which
just forwards the call.

Example:

concept C <typename T> { void foo(T&); }

concept_map C<int> { void foo(int& i) {++i;} }

would result in the following imaginary definitions for the scope of
the template [temp.archetype]:

struct int';
void foo (int'& i) { ++i; }
struct int'
{
    // Whatever should be here without this change
    // Which is a bunch of deleted operators and special functions
    void foo () & {foo(this);}
}

Personally, I'd prefer the former, as it gives member function
requirements a purpose - the latter is more of a way around the
problem.

Sean Hunt

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Sean Hunt <rideau3@gmail.com>
Date: Thu, 13 Dec 2007 04:43:52 CST
Raw View
Given my understanding of the description of n2421, there are only
three good cases whereby you should actually use an associated member
definition. In all other cases, there is a superior, more compatible
alternative (I'll get to that in a moment). Each of these cases is
when there is absolutely no alternative - constructors, destructors,
and assignment operators. This is because a concept map is forbidden
from defining or declaring an associated member function. Rather than
declaring the following:

concept C <typename T>
{
     void T::foo();
}

You should declare:

concept C <typename T>
{
     void foo (T& t) { late_check { t.foo(); } }
}

(The use of the late_check is due to my understanding of [concept.fct]
- the default implementation is a constrained template and so must use
late_check to avoid the use of the archetype T', which doesn't include
a member function foo. If this is incorrect, I would appreciate being
corrected.)

The latter version is identical to the first from the user's
perspective. He can map the concept (or not, if it's auto) the same.
But if he needs to change the implementation - crucial if he doesn't
have access to the class in question - he can do so by simply
modifying the function definition in the concept map. The template
implementer just needs to rearrange the syntax, which is the heart of
my disagreement.

See, the problem is that this syntax represents a major step backward.
Now, rather than using the object-oriented "." operator, one has to
use the old, C-style method of passing the object itself. (there are
differences like the use of references, but that's not the important
point). Proper OO is one of the major attractions of C++ - it doesn't
make sense to make that mutually exclusive with constrained templates.
If you try to use the first of my coding examples, allowing you to
have a constrained template that interfaces in an object-oriented
manner, then you sacrifice a third of C++'s (and C's) major axioms (no
pun intended) - the avoidance of naming requirements for users.

My personal favored solution would be to allow a concept map (or
concept default implementation) to define a member function for the
archetype for any member function other than an assignment operator,
constructor, or destructor. Special rules for definition of other
functions within maps that must otherwise be members exist, clearly to
protect those three functions. Wouldn't it just be better to actually
protect them from instantiation? The only other reason I can think of
is to prevent giving a builtin type a member function, but since
concepts already abstract that, I don't see how that could be a
problem (barring stupid uses of late_check).

Alternatively, it could be changed so that in the archetype T' of T,
for each associated non-member function whose first parameter is a
reference to T, T' has a member function of the same name, with the
same reference qualifier as the associated function's first parameter,
and with parameters of the same types as the other parameters, which
just forwards the call.

Example:

concept C <typename T> { void foo(T&); }

concept_map C<int> { void foo(int& i) {++i;} }

would result in the following imaginary definitions for the scope of
the template [temp.archetype]:

struct int';
void foo (int'& i) { ++i; }
struct int'
{
     // Whatever should be here without this change
     // Which is a bunch of deleted operators and special functions
     void foo () & {foo(this);}
}

Personally, I'd prefer the former, as it gives member function
requirements a purpose - the latter is more of a way around the
problem.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]