Topic: Should this short but abstruse program compile?


Author: Howard Gardner <hgardner@rawbw.com>
Date: Fri, 21 Jul 2006 11:39:11 CST
Raw View
Comeau looked into this. They said they think that the error is valid
now, but that it's an open issue.

http://www.comeaucomputing.com/iso/cwg_active.html#339

To avoid confusion if some other hapless soul finds this thread, here is
the code that they looked at.

#include <ostream>
#include <cstddef>

template< typename x >
  x * faux_ptr();

template< typename x, size_t = sizeof( faux_ptr< x >() ) >
  struct tpt{};

template< typename x >
  bool fnc( tpt< x > );

tpt< int > inst;

size_t size = sizeof( fnc( inst ) );

int main()
{
  using namespace std;
  cout << size << endl;
}

---
[ 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: "Richard Smith" <richard@ex-parrot.com>
Date: Tue, 18 Jul 2006 10:14:43 CST
Raw View
Howard Gardner wrote:
> Richard Smith wrote:
> > Howard Gardner wrote:
> >> template< typename x, size_t = sizeof( faux_ptr< x >() ) >
> >>    struct tpt{};
> >>
> >> template< typename x >
> >>    bool fnc( tpt< x > );
> >
> > This is a long-standing bug in gcc:
> >
> >   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=4926
> >   http://gcc.gnu.org/ml/gcc/2003-06/msg01948.html
> >
> > The code should certainly compile.
> >
>
> It doesn't work in Comeau 4.3.3 either. Victor Bazarov told me that the
> original code will compile in VC++ 2005.

Well, I believe this to be a bug in these compilers.  Comeau's error
message says:

error: no instance of function template "fnc" matches the argument list
       The argument types that you used are: (tpt<int, 4U>)

  size_t size = sizeof( fnc( inst ) );

That is, it doesn't believe the type tpt<int> matches the function
declaration

  template< typename x > bool fnc( tpt<x> );

I can't see anything in the standard to justify this.  It's notable
that if you change the declaration of tpt to

  template< typename x, size_t = sizeof(x*) >
  struct tpt {};

it now compiles.

> Below is a version that's more closely related to my motivation. [...]

> template< typename x, size_t = sizeof( faux_ptr< x >()->memvar ) >
>    struct tpt{};
>
> template< typename x > no fnc( ... );
>
> template< typename x > yes fnc( tpt< x > * );

This question has come up before and is rather different to your
previous example.  In the previous example, the sizeof expression was
always well-formed; in this example, you are hoping to exploit the
well-formedness of the sizeof expression to trigger SFINAE.  This
relies on the interpretation of 14.8.2, para 2, bullet 3:

"... If a substitution in a tempalte parameter or in the function type
of the function template results in an invalid type, type deduction
fails. ... Type deduction may fail for the following reasons: [long
list]."

The implication (and, I gather, intention) is that the list is an
exhaustive list of ways in which substitution might result in a invalid
type.  Unfortunately this is now known not to be exhaustive (cf DRs 337
and 368);  invalid sizeof expressions are another example where an
obviously invalid type can be created, but is not covered by 14.8.2.

See this thread for a more lengthy discussion:

http://groups.google.com/group/comp.std.c++/tree/browse_frm/thread/31ab63814c9b26b8/c09dd1297c02d267

--
Richard Smith

---
[ 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: hgardner@rawbw.com (Howard Gardner)
Date: Tue, 18 Jul 2006 16:51:12 GMT
Raw View
Richard Smith wrote:
> Howard Gardner wrote:
>> Richard Smith wrote:
> This question has come up before and is rather different to your
> previous example.  In the previous example, the sizeof expression was
> always well-formed; in this example, you are hoping to exploit the
> well-formedness of the sizeof expression to trigger SFINAE.  This
> relies on the interpretation of 14.8.2, para 2, bullet 3:
>
> "... If a substitution in a tempalte parameter or in the function type
> of the function template results in an invalid type, type deduction
> fails. ... Type deduction may fail for the following reasons: [long
> list]."
>
> The implication (and, I gather, intention) is that the list is an
> exhaustive list of ways in which substitution might result in a invalid
> type.  Unfortunately this is now known not to be exhaustive (cf DRs 337
> and 368);  invalid sizeof expressions are another example where an
> obviously invalid type can be created, but is not covered by 14.8.2.
>
> See this thread for a more lengthy discussion:
>
> http://groups.google.com/group/comp.std.c++/tree/browse_frm/thread/31ab63814c9b26b8/c09dd1297c02d267

Thanks Richard. Very helpful. (And heartbreaking.)

---
[ 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: "Richard Smith" <richard@ex-parrot.com>
Date: Mon, 17 Jul 2006 09:44:59 CST
Raw View
Howard Gardner wrote:
> template< typename x, size_t = sizeof( faux_ptr< x >() ) >
>    struct tpt{};
>
> template< typename x >
>    bool fnc( tpt< x > );

This is a long-standing bug in gcc:

  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=4926
  http://gcc.gnu.org/ml/gcc/2003-06/msg01948.html

The code should certainly compile.

--
Richard Smith

---
[ 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: Howard Gardner <hgardner@rawbw.com>
Date: Mon, 17 Jul 2006 12:17:44 CST
Raw View
Richard Smith wrote:
> Howard Gardner wrote:
>> template< typename x, size_t = sizeof( faux_ptr< x >() ) >
>>    struct tpt{};
>>
>> template< typename x >
>>    bool fnc( tpt< x > );
>
> This is a long-standing bug in gcc:
>
>   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=4926
>   http://gcc.gnu.org/ml/gcc/2003-06/msg01948.html
>
> The code should certainly compile.
>

It doesn't work in Comeau 4.3.3 either. Victor Bazarov told me that the
original code will compile in VC++ 2005.

Below is a version that's more closely related to my motivation. It will
compile, but doesn't work right on either Comeau or VC++ (Victor tested
it for me there.) On Comeau, I think that it is failing for the same
reason that the original code is failing. No clue on why the original
code would work on VC++, but this code would misbehave.

It's a real shame that it doesn't work, because I think it would allow
the implementation of a very nice non-intrusive introspection facility.
The code below is meant to check for the presence of a member variable.
I think that it would be possible to write similar code to check for a
member function, a non-member function that takes "x" as a parameter,
and so on. (Of course, I can't perform any experiments means so I can't
test that hypothesis.)

Code like that could be used with enable_if to great effect:

1) It would let a template implementer manage function overloads and
template specializations with great precision; possibly even with as
much precision as the proposed concept facility will deliver.

2) It would probably improve the utility of diagnostic messages. If the
template user calls for a function template instantiation that can't
work, the diagnostic will probably say "no match" and refer to the line
on which he makes the call. If he calls for a class template
instantiation that can't work, the diagnostic will probably complain
about an incomplete type and point to the line on which he tries to use
the type. If the diagnostic is complaining about some line deep inside
your template, it means the template implementer missed a requirement.

3) It wouldn't throw any extra work at the template user. He's got to
provide the proper syntax in the first place, and the template
implementer is able to check for that syntax very directly.

It appears to me that the practical differences between an enable_if
approach like this and the new concept facility are:

1) The new concept facility might deliver more precise diagnostics to
the template user by naming the unmodeled concept. On the other hand,
compilers might decide to mention the line on which the where clause
resides--or some line in a concept definition--rather than the line that
contains the offending usage. If compilers do that, then I think that
the concept facility diagnostics are much less useful than enable_if
diagnostics.

2) It might be possible to express syntax requirements using the concept
facility that could not be expressed in an enable_if facility. Maybe
there are things that just can't be shoehorned into a sizeof expression.

3) The concept facility will require less complex code from the template
implementer. The concept definitions look straightforward and the where
clause Even if the code I'm considering works, it's pretty abstruse.

4) The enable_if approach cries out for packaging in a macro analogous
to boost::mpl's HAS_XXX_TRAIT_DEF.

All of the differences that I can see militate in favor of the concept
facility, except one: the potential that compiler diagnostics will
mention the wrong line. I earnestly hope that they don't do that,
because it would damage this facility if they did.

Anyway, here's the code:

#include <ostream>
#include <cstddef>

struct has{ int memvar; };

template< typename x >
   x * faux_ptr();

template< typename x, size_t = sizeof( faux_ptr< x >()->memvar ) >
   struct tpt{};

typedef char no;
struct yes{ no val[2]; };

template< typename x >
   no fnc( ... );

template< typename x >
   yes fnc( tpt< x > * );

int main()
{
   using namespace std;
   cout << sizeof( fnc< has >( 0 ) ) << endl;
}

On my compiler, sizeof( no ) is 1, and sizeof( yes ) is 2.

The program selects the "no" version of fnc and prints:

1

I think that it should select the "yes" version of fnc and print:

2

---
[ 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: Howard Gardner <hgardner@rawbw.com>
Date: Fri, 14 Jul 2006 09:57:18 CST
Raw View
/*

As it sits, this program won't compile for me. It will compile
using either of the versions of tpt that I've commented out.
Which versions SHOULD compile?

I think that the code is interesting because I think that it is
on the path to building a useful non-intrusive type introspection
facility. The version that uses "has" indicates the direction.

*/
#include <ostream>
#include <cstddef>

template< typename x >
   x * faux_ptr();

//template< typename x, size_t = sizeof( int * ) >
//  struct tpt{};

//template< typename x, size_t = sizeof( faux_ptr< int >() ) >
//  struct tpt{};

//struct has{ int memvar; };
//template< typename x, size_t = sizeof( faux_ptr< has >()->memvar ) >
//  struct tpt{};

template< typename x, size_t = sizeof( faux_ptr< x >() ) >
   struct tpt{};

template< typename x >
   bool fnc( tpt< x > );

tpt< int > inst;

size_t size = sizeof( fnc( inst ) );

int main()
{
   using namespace std;
   cout << size << endl;
}

---
[ 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: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Sat, 15 Jul 2006 07:52:58 CST
Raw View
Howard Gardner wrote:
> /*
>
> As it sits, this program won't compile for me. It will compile
> using either of the versions of tpt that I've commented out.
> Which versions SHOULD compile?
>
> I think that the code is interesting because I think that it is
> on the path to building a useful non-intrusive type introspection
> facility. The version that uses "has" indicates the direction.
>
> */
> #include <ostream>
> #include <cstddef>
>
> template< typename x >
>    x * faux_ptr();
>
> //template< typename x, size_t = sizeof( int * ) >
> //  struct tpt{};
>
> //template< typename x, size_t = sizeof( faux_ptr< int >() ) >
> //  struct tpt{};
>
> //struct has{ int memvar; };
> //template< typename x, size_t = sizeof( faux_ptr< has >()->memvar ) >
> //  struct tpt{};
>
> template< typename x, size_t = sizeof( faux_ptr< x >() ) >
>    struct tpt{};
>
> template< typename x >
>    bool fnc( tpt< x > );
>
> tpt< int > inst;
>
> size_t size = sizeof( fnc( inst ) );
>
> int main()
> {
>    using namespace std;
>    cout << size << endl;
> }
>
Well, the WG21 standard is pretty clear.

" 5.19  Constant expressions
[expr.const]

1 In  several places, C++ requires expressions that evaluate to an
inte-
  gral  or  enumeration  constant:   as   array   bounds
(_dcl.array_,
  _expr.new_), as case expressions (_stmt.switch_), as bit-field
lengths
  (_class.bit_), as enumerator initializers (_dcl.enum_), as static
mem-
  ber initializers (_class.static.data_), and as integral or
enumeration
  non-type template arguments (_temp.arg_).
  constant-expression:
          conditional-expression
  An integral constant-expression can involve only  literals
(_lex.lit-
  eral_),  enumerators,  const variables or static data members of
inte-
  gral  or  enumeration  types  initialized  with  constant
expressions
  (_dcl.init_),  non-type template parameters of integral or
enumeration
  types, and sizeof expressions.   Floating  literals  (_lex.fcon_)
can
  appear  only  if they are cast to integral or enumeration types.
Only
  type conversions to integral or enumeration types  can  be  used.
In
  particular,  except  in  sizeof expressions, functions, class
objects,
  pointers, or references shall not be used, and assignment,
increment,
  decrement, function-call, or comma operators shall not be used."


"

  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 of integral or enumeration type; or

  --the name of a non-type template-parameter; or

  --the name 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; or

  --the address of an object or function with external linkage,
includ-
    ing  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

  --a pointer to member expressed as described in _expr.unary.op_ .
"

> //template< typename x, size_t = sizeof( int * ) >
> //  struct tpt{};
>
That should compile (the non-type template argument is a constant
expression).

> //template< typename x, size_t = sizeof( faux_ptr< int >() ) >
> //  struct tpt{};
>
OK, should compile.

> //struct has{ int memvar; };
> //template< typename x, size_t = sizeof( faux_ptr< has >()->memvar ) >
> //  struct tpt{};
>
It is not a constant expression, nor anything listed in
temp.arg.nontype. It must not compile.

> template< typename x, size_t = sizeof( faux_ptr< x >() ) >
>    struct tpt{};
>
It is a constant expression; it should compile.

This last one doesn't compile with GCC 4.0.2.
It says:
"sorry, unimplemented: call_expr cannot be mangled due to a defect in
the C++ ABI"

ICC 9.0 is not able to deduce the template argument in the call to fnc:
"error: no instance of function template "fnc" matches the argument
list
            argument types are: (tpt<int, 4U>)
  size_t size = sizeof( fnc( inst ) );"

---
[ 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: Howard Gardner <hgardner@rawbw.com>
Date: Sat, 15 Jul 2006 17:29:57 CST
Raw View
"In particular, except in sizeof expressions, functions, class objects,
pointers, or references shall not be used, and assignment, increment,
decrement, function-call, or comma operators shall not be used."

I think that they are *all* sizeof expressions, so this actually says
nothing about them.

"A  template-argument  for  a non-type, non-template template-parameter
shall be one of:

   --an integral constant-expression of integral or enumeration type"

I think that they are *all* integral constant expressions of integral or
enumeration type.

---
[ 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                      ]