【问题标题】:Anonymous struct in typedef of trait class特征类的 typedef 中的匿名结构
【发布时间】:2011-05-09 16:02:04
【问题描述】:

对不起,这个有趣的标题。

在 C++0x 之前,restrictions 使用函数局部结构(“局部类型”)作为模板参数。我的问题本质上是类似的限制是否适用于匿名结构。具体来说,在 trait 类的上下文中:

template <typename T>
struct trait;

template <>
struct trait<int> {
    typedef int type;
};

template <typename T>
struct trait<std::basic_string<T> > {
    typedef struct {
        T value;
    } type;
};

trait<std::string>::type foo; // Is this valid?

template <typename T>
void f() { }

f<trait<std::string>::type> >(); // Is this?

template <typename T>
void g() { f<typename trait<T>::type>(); }

g<std::string>(); // And this?

这是有效和可靠的吗?它可以在最新版本的 GCC 和 LLVM 中编译,但我仍然不确定这是否严格有效,以及它是否被 VC++ 和 ICC 理解。

【问题讨论】:

  • 由于所讨论的类型不用作模板参数,我不确定这与本地类型的限制有何关系。
  • @Luc 我将添加一个示例。但问题本身就存在。以上有效吗?如果是这样,它对模板参数有效吗?
  • 现在我想知道如果您创建一个匿名结构的实例并将其用作参数会发生什么。 (您可以使用decltype 获取其类型)
  • f&lt;trait&lt;std::string&gt;::type&gt; &gt;(); 肯定是无效的:D。 (还有一个额外的&gt;
  • 参见open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#62(当然,项目符号列表在 C++0x 中完全消失了。但请注意他们在那段时间添加的条目,以便清楚地了解用于链接目的的名称)。跨度>

标签: c++ templates c++11 anonymous-class


【解决方案1】:

作为参考,引用 14.3.1/2 中的链接问题:

本地类型,没有链接的类型, 未命名类型或复合类型 从任何这些类型不得 用作 a 的模板参数 模板类型参数。

我的解释是typedef struct 正在创建一个未命名类型的别名,因此它不能用作模板类型参数。进一步注意,在 C 中,typedef struct {} Foo; 的处理方式与 struct Foo {}; 的处理方式截然不同,因为这两种形式不等价(尽管 C++ 中没有出现这种差异)。

因此,您的第一个示例似乎有效(因为它没有使用未命名类型作为模板类型参数),而第二个和第三个示例在技术上是无效的(因为它们确实将其用作模板类型参数)。

最后我要问的是,你有什么理由不能命名结构而不是typedefing它?

编辑:从 7.1.3/1 开始:

...因此,typedef-name 是 另一种类型。 typedef-name 不 以类的方式引入新类型 声明(9.1)或枚举声明 确实...

这强烈暗示以这种方式使用typedef 不会引入适合用作模板类型参数的类型。

【讨论】:

  • “你不能命名这个结构有什么原因吗?”……相当多(!)的冗余输入。但除此之外,没有。所以我会在安全和标准合规方面犯错。
  • @Konrad :呃,typedef struct {} type; 不是比struct type {}; 更冗余的输入吗?还是我错过了什么?
  • @Xeo 你是不是在公共场合骂我傻? :-p 该死,有时你只见树木不见林。
【解决方案2】:

在即将发布的标准中,该限制已从语言中移除。该标准在

中说

14.3.1 [temp.arg.type] /1

作为类型的模板参数的模板参数应该是类型ID。

typedef 是一个有效的type-id。事实上,下一段包含这样一个例子:

14.3.1 [temp.arg.type] /2

template <class T> class X { };
template <class T> void f(T t) { }
void f() { 
   typedef struct { } B;
   B b;
   X<B> x3;
   f(b);
}

(我已经修剪了大部分其他示例)该示例表明,未命名的类型可以用作类模板和函数模板中的类模板参数。

【讨论】:

  • 啊!终于有权威了。我想我们可以推断,如果函数中的匿名类型有效,那么结构中的匿名类型也应该使用 :)
  • 您对 14.3.1 [temp.arg.type] /1 的引用是不够的。它并不是说每个 type-id 都是有效的模板参数。事实上,X&lt;struct { }&gt; 也使用了 type-id,但仍然无效。
  • @Johannes Schaub:我已经审查过了,你说得对,struct {}type-id(我不认为是),然而,标准的第二个引用(实际示例)提供了一个正在使用的标准中的示例,虽然不是规范性的,但似乎表明它是允许的。
【解决方案3】:

typedef 声明定义了一个匿名类和该类的 typedef-name,typedef-name 是用于链接目的的类的名称。因此,如果该类满足其他条件,则将该类用作模板参数是合法的。

参见 C++03 标准的 7.1.3p5

如果 typedef 声明定义了一个 未命名的类(或枚举),第一个 typedef-name 由声明声明为该类类型(或枚举 type) 用于表示类类型 (或枚举类型)用于链接目的 仅(3.5)。 [示例:

typedef struct { } *ps, S; // S is the class name for linkage purposes

这是 C++0x FDIS 中的 7.1.3p9。

FWIW,此代码可以使用 MSVC2010 编译(模拼写错误)。

【讨论】:

  • 要明确一点:所以这取代了 8.1.3p1(由 Mark 引用),并且带有 typedef-name does 的未命名类有链接吗?
  • 随它去。 typedef 本身并没有引入新类型,而是 typedef 声明“内部”的类定义。然后 typedef 给出新类型的名称。它不是类型的“名称”——例如,您不能在类定义中使用它来定义构造函数或析构函数——但它提供了一个“用于链接目的”的名称。
【解决方案4】:

嗯,这相当于

template <typename T>
struct trait<std::basic_string<T> > {
    struct type {
        T value;
    };
};

这是完全合法的。

【讨论】:

  • 其实我在想,我会认为它们是等价的,但它是否被标准定义为等价的?
  • 其实它们是相似的,但并不等同。答案中的代码在用户定义类型的标识符空间中定义了trait&lt;T&gt;::type,而问题中的代码没有添加这样的标识符,只会添加该标识符空间的别名outside。 typedef 仍将引用没有名称的类型(在用户定义的标识符空间中),并且可能与问题非常相关。
猜你喜欢
  • 2016-07-31
  • 2012-09-11
  • 2019-06-02
  • 2019-09-28
  • 1970-01-01
  • 2013-07-17
  • 1970-01-01
  • 2020-04-29
  • 1970-01-01
相关资源
最近更新 更多