typedef是必需的情况有哪些?

36

考虑来自安全布尔值习惯用法的以下摘录:

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

能否在不使用typedef的情况下声明转换函数?以下代码无法编译:

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

3
为什么你会在不使用typedef的情况下声明函数? - Cheers and hth. - Alf
转换为安全布尔值是否绝对必要? - Tadeusz Kopec for Ukraine
@Tad:在我的特定情况下(一个optional<T>类模板),它似乎很有用。 - fredoverflow
2
@Tad:我自己经常使用安全布尔习惯用法。@Fred:我要注意一下,typedef有助于产生更易读的代码/错误。 - Matthieu M.
2
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#395 - Johannes Schaub - litb
显示剩余2条评论
8个回答

9
啊,我刚想起了 identity 元函数。可以这样编写:
operator typename identity<void (Testable::*)() const>::type() const;

在以下 identity 的定义中:

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

你可以认为identity仍然使用了typedef,但对我来说,这个解决方案已经足够"好"了。

我本来要立刻回答的... identity 是一个不错的方法,可以避免解析问题。 - David Rodríguez - dribeas
@David:遗憾的是,identity 不是标准 C++0x 的一部分。在这种情况下,我们可以使用 std::decay... - fredoverflow
这是我想要的更好版本! - AJG85

3

一个例子(与你的问题无关)需要使用typedef的情况是使用va_arg()宏时。引用C99标准(7.15.1.1):

type* va_arg(va_list ap, type);

...

参数type应该是指定类型名称,可以通过在type后面加上*来获得指向具有指定类型的对象的指针的类型。


1
还有另一种情况,即伪析构函数的调用。 - Cheers and hth. - Alf
不允许使用p->~unsigned char()(对于那些不知道伪析构函数是什么或为什么需要在那里使用typedef的人)。 - MSalters

3

回答问题标题中的“typedef是否绝对必要?”这个问题,这里有一个需要使用typedef的例子:

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

在此查看结果:http://ideone.com/JPUra

编译器之争又来了!GCC失败了,VC成功了。 - Ajay
1
f(identity<unsigned char>::type()) 怎么样? ;) - fredoverflow
2
@FredOverflow 看起来它会工作,尽管从技术上讲 type 是一个 typedef。 :P - user802003

2
在C++11中,您可以这样做(gcc 4.5.2):
operator decltype((void (Testable::*)() const)(0))() const ;

我并不是说这很漂亮...


你尝试过使用decltype(&Testable :: foo)吗?其中foo是具有适当签名的成员方法? - David Rodríguez - dribeas

2
我的分析表明,如果不使用 typedef,则不可能实现。编译器将看到 ( 作为第一个标记,并假定您正在重载 () 运算符,该运算符不应具有任何参数(下一组括号中会出现参数)。添加任何一组额外的括号也无济于事,实际上会让编译器混淆,从而导致更多错误。
大多数 STL 代码都基于 typedef 定义,我们应该/必须使用它们!

2

在您的情况下,语法要求使用 typedef。一个conversion-function-id必须是operatorconversion-type-id的形式。 conversion-type-id不能包含括号。因此,当转换为指向函数类型或指向成员函数类型时,必须使用typedef。


“conversion-type-id不能包含括号。”你从哪里得到的? - TonyK
@TonyK:从语法上讲,它基本上是一个可能的const/volatile原始类型或限定符id(类型说明符序列),后面跟着零个或多个指针运算符(转换声明符-可选)。 - n. m.
@n:我找到了一种方法:template <int T> class C { } ; class S { operator C<(99)>*() { return 0 ; } } ; - TonyK

1

typedef不是宏,你的第二个例子与第一个不等价。在第一个情况下,你的typedef定义了一个函数对象,然后在函数对象类型的转换运算符中使用该类型。在第二种情况下,操作符使用了错误的语法,因为没有指定操作符,因为没有类型。我不确定如何编写它,但通常有一种方法。

除了在TMP中使代码易读之外,typedef并不是真正必要的,甚至这也取决于你是什么样的人类。

由于我想不出替代语法,也许在某些情况下typedef是必要的。我刚想到另一个可能性。假设你有一个包含具有以下返回类型的静态方法的特化模板:

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(); }
};

我认为在这种情况下,你也需要typedef,以便无论特化如何调用静态方法,否则方法可能会让编译器混淆,因为返回类型将不同,但它不是一个合适的重载。

template <typename T>
struct WhateverUser
{
   typename WhateverHandler<T>::rType DoWhatever()
   {
       return WhateverHandler<T>::template Whatever();
   }
};

1

我刚刚遇到了这个问题,使用clang++:

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

还有一个 C++11 STL 模板可以覆盖 identity<T> 功能:

#include <type_traits>struct foo {
     void bar( ) const { }
     operator std::common_type<void(foo::*)( )const>::type( ) { return &foo::bar; }
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接