【问题标题】:decltype and the scope operator in C++C++ 中的 decltype 和范围运算符
【发布时间】:2012-02-15 10:06:48
【问题描述】:

我需要获取实例化模板时提供的类型。考虑以下示例:

template <typename T> struct Foo
{
  typedef T TUnderlying;
};

static Foo<int> FooInt;

class Bar
{
public:
  auto Automatic() -> decltype(FooInt)::TUnderlying
  {
    return decltype(FooInt)::TUnderlying();
  }
};

int main()
{
  Bar bar;
  auto v = bar.Automatic();
    return 0;
}

此代码的问题是与 decltype 一起使用范围运算符。 Visual C++ 2010 抱怨如下:

错误 C2039:“TUnderlying”:不是“全局命名空间”的成员

我在 Wikipedia 上收集了有关该主题的一些信息:

在评论正式的 C++0x 委员会草案时,日本 ISO 成员团体指出“范围运算符 (::) 不能应用于 decltype,但它应该是。在这种情况下将很有用从实例中获取成员类型(嵌套类型)如下“:[16]

vector<int> v;
decltype(v)::value_type i = 0; // int i = 0;

David Vandevoorde 解决了这个问题和类似问题,并于 2010 年 3 月投票加入了工作文件。

所以我认为 Visual C++ 2010 没有实现此功能。我想出了这个解决方法:

template <typename T> struct ScopeOperatorWorkaroundWrapper
{
  typedef typename T::TUnderlying TTypedeffedUnderlying;
};

auto Automatic() -> ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying
{
  return ScopeOperatorWorkaroundWrapper<decltype(FooInt)>::TTypedeffedUnderlying();
}

我是否错过了更优雅、更简洁的解决方案?

【问题讨论】:

  • 你试过FooInt::TUnderlying而不是decltype(FooInt)::TUnderlying吗?我看不到您希望通过decltype 获得什么。
  • 你添加的只是ScopeOperatorWorkaroundWrapper&lt;&gt;,我不知道你想要多少“不那么冗长”。毕竟,它是一种解决方法。

标签: c++ scope c++11 operator-keyword decltype


【解决方案1】:

这用基于模板的解决方法透明地替换了decltype 关键字。一旦您不再需要支持 MSVC2010,您可以在不更改任何用户代码的情况下删除宏定义:

#if _MSC_VER == 1600
#include <utility>
#define decltype(...) \
  std::identity<decltype(__VA_ARGS__)>::type
#endif

这允许它在 MSVC10 上编译和工作:

std::vector<int> v;
decltype(v)::value_type i = 0;

请注意,std::identity 不是 C++ 标准的一部分,但在这里依赖它是安全的,因为解决方法仅限于在其标准库实现中包含 std::identity 的编译器。

【讨论】:

  • 使用可变参数宏来解决逗号问题。 #define decltype(...) detail::type_helper&lt;decltype(__VA_ARGS__)&gt;::type。请注意,decltype(x) 可能与decltype((x)) 不同,因此您的t2 可能会导致不同的类型。
  • @KennyTM:谢谢,我已将其纳入答案中。
  • 一旦编译器实现了这个特性,我认为这个解决方案是侵入性最小且最容易删除的。感谢您的宝贵时间!
  • 不幸的是,带有 VA__ARGS 的版本无法在 MSVC 下编译(错误 C2065: 'VA__ARGS' : undeclared identifier),所以我将不得不坚持使用 #define decltype(exp) ,但这对我来说仍然很好。
  • 它对我来说编译得很好。您是否完全按照书面说明使用它? __VA__ARGS__ 中的前导和尾随下划线很重要。
【解决方案2】:

解决方法看起来相对不错,但它不可扩展,而且名称很糟糕1。为什么不使用id

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

然后:

id<decltype(FooInt)>::type::TUnderlying;

未经测试,但应该可以工作。


1 太冗长了,即使他们描述这是一种解决方法,这可能是多余的,在大多数情况下都不是有用的信息。

【讨论】:

  • 感谢您的意见。关于名称 - 这只是一个示例,我承认名称可能会更短一些,但我更喜欢能够捕捉问题推理的名称。普通的“id”稍后会(当我忘记它时)告诉我这只是一个临时的黑客攻击。我认为使用现代自动完成功能没有必要让我的代码难以用神秘的缩写来理解。
  • @Milan id 是这个元函数的正确名称(它是 identity function),我认为它实际上已经在 C++11 中的某个地方定义,正好作为一种语法变通方法不能使用普通类型的地方。无论哪种方式,您应该使用这个名称,因为它是这个概念的既定名称,并且通常被理解。这绝不是一个“神秘的缩写”(我同意永远不要使用它)。
  • @Milan 但是既然你提到了自动完成:避免长名称的原因是 不是 键入它们需要很长时间(自动完成在这里有帮助,真的)。但这也使代码不可读,并且应该始终首选简洁(简短但准确)的名称。
  • VC++ 2010 带有std::identity&lt;&gt;,正是为了这个目的。
  • @ildjarn 不幸的是,我在 C++11 标准草案中找不到它。但是,预览中的搜索很糟糕,所以它可能在那里。
【解决方案3】:

作为替代方案,您可以使用函数模板助手轻松提取类型:

template <typename T> struct Foo
{
    typedef T TUnderlying;
};

static Foo<int> FooInt;

template <typename T>
typename Foo<T>::TUnderlying foo_underlying(Foo<T> const &)
{
    return typename Foo<T>::TUnderlying();
}

class Bar
{
public:
//    auto Automatic() -> decltype(FooInt)::Underlying
//    {
//        return decltype(FooInt)::Underlying;
//    }
    auto Automatic() -> decltype(foo_underlying(FooInt))
    {
        return foo_underlying(FooInt);
    }
};

int main()
{
    Bar bar;
    auto v = bar.Automatic();
}

【讨论】:

    猜你喜欢
    • 2011-07-28
    • 1970-01-01
    • 2021-09-26
    • 2011-01-21
    • 2011-04-24
    • 2013-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多