Are there cases where a typedef is absolutely necessary?
Asked Answered
M

8

36

Consider the following excerpt from the safe bool idiom:

typedef void (Testable::*bool_type)() const;
operator bool_type() const;

Is it possible to declare the conversion function without the typedef? The following does not compile:

operator (void (Testable::*)() const)() const;
Monosaccharide answered 9/8, 2011 at 15:30 Comment(7)
Why would you declare the function without the typedef?Mair
Is conversion to safe bool absolutely necessary?Clintonclintonia
@Tad: It seems useful in my particular case (an optional<T> class template).Monosaccharide
@Tad: I myself use the safe bool idiom fairly often, @Fred: I would note that the typedef helps producing more readable code/errors.Sniperscope
open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#395Decuple
Don't treat me so seriously. It was a failed attempt towards a pundit badge :-)Clintonclintonia
@Johannes: But template <typename T> using id = T; isn't in the standard library, right? :(Monosaccharide
M
9

Ah, I just remembered the identity meta-function. It is possible to write

operator typename identity<void (Testable::*)() const>::type() const;

with the following definition of identity:

template <typename T>
struct identity
{
    typedef T type;
};

You could argue that identity still uses a typedef, but this solution is "good" enough for me.

Monosaccharide answered 9/8, 2011 at 17:3 Comment(3)
I was going to answer right this... identity is a nice way of getting around parsing problems.Brittney
@David: Sadly, identity is not part of standard C++0x. In this case, we could use std::decay, though...Monosaccharide
That's a better version of what I was going for!Westernize
I
3

One case (unrelated to your question) where a typedef is required is when using the

va_arg() macro. Quoting the C99 standard (7.15.1.1):

type* va_arg(va_list ap, type);

...

The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type

Imide answered 9/8, 2011 at 16:16 Comment(2)
there's another such case, namely call of pseudo-destructorMair
I.e. p->~unsigned char() isn't allowed ( for those who don't know what a pseudo-destructor is or why you'd need a typedef there).Ortiz
D
3

Answering the "Are there cases where a typedef is absolutely necessary?" from the question title, here is one example of where a typedef is needed:

f(unsigned char());   // compiler error!
typedef unsigned char Byte;
f(Byte());            // fine!

See the results here: http://ideone.com/JPUra

Danilodanio answered 9/8, 2011 at 16:20 Comment(3)
Compiler war again! GCC fails, VC succeeds.Ligature
How about f(identity<unsigned char>::type())? ;)Monosaccharide
@FredOverflow that looks like it'll work, although technically type is a typedef. :PDanilodanio
L
2

My analysis says that it is not possible without using typedef. The compiler sees ( as the first token and assumes you are overloading () operator, which shouldn't have any arguments (The arguments would come in next set of parenthesis). Putting any set of extra parenthesis wouldn't help either - but would actually confuse the compiler and hence set of more errors.

Most of the STL code is on top of typedefinitions, and we should/must use them!

Ligature answered 9/8, 2011 at 15:47 Comment(0)
L
2

It seems that the grammar demands using a typedef in your case. A conversion-function-id must be of the form operator conversion-type-id. The conversion-type-id cannot contain parentheses. So you must use typedef when converting to a pointer-to-function type, or to a pointer-to-member-function type.

Lesbianism answered 9/8, 2011 at 16:37 Comment(3)
"The conversion-type-id cannot contain parentheses." Where do you get that from?Typology
@TonyK: from the grammar. It is basically a maybe const/volatile primitive type or a qualified id (type-specifier-seq), followed by zero or more pointer-operators (conversion-declarator-opt).Lesbianism
@n: I found a way: template <int T> class C { } ; class S { operator C<(99)>*() { return 0 ; } } ;Typology
T
2

In C++11, you can do it like this (gcc 4.5.2):

operator decltype((void (Testable::*)() const)(0))() const ;

I'm not saying it's pretty...

Typology answered 9/8, 2011 at 16:53 Comment(1)
Have you tried with decltype( &Testable::foo ) where foo is a member method with the appropriate signature?Brittney
W
1

A typedef is not a macro your second example is not equivalent to the first. In the first case your typedef is defining a functor then using that type in a cast operator of the functor type. In the second the operator is using bad syntax as there is no operator specified because there is no type. I'm not sure how to write it but there is a way usually.

Typedefs aren't really necessary except for making human readable code in TMP and even then it depends on what kind of human you are.

Since I can't come up with the alternative syntax maybe typedefs are necessary in some cases. I just thought of another one possibly. Say you had a template with specializations which contained a static method with a return type like below:

template <typename T>
struct WhateverHandler
{
   typedef T rType;
   static rType Whatever() { return rType(); }
};

template <>
struct WhateverHandler<std::string>
{
   typedef std::string rType;
   static rType Whatever() { return rType(); }
};

I think in this case also you would need the typedef in order to call the static method regardless of specialization as otherwise the method could confuse the compiler because the return types would differ but it wouldn't be a proper overload.

template <typename T>
struct WhateverUser
{
   typename WhateverHandler<T>::rType DoWhatever()
   {
       return WhateverHandler<T>::template Whatever();
   }
};
Westernize answered 9/8, 2011 at 15:51 Comment(0)
D
1

I just ran across this issue, with clang++:

foo.cpp:17:8: error: must use a typedef to declare a conversion to 'void (*(int))()'

and there's a C++11 STL template which covers the identity<T> functionality:

#include <type_traits>
…
struct foo {
     void bar( ) const { }
     operator std::common_type<void(foo::*)( )const>::type( ) { return &foo::bar; }
};
Decagram answered 7/3, 2014 at 23:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.