【问题标题】:C++ syntax to call templated inner class static member function?调用模板化内部类静态成员函数的 C++ 语法?
【发布时间】:2010-01-19 07:32:04
【问题描述】:

我有一些模板代码在 VC9 (Microsoft Visual C++ 2008) 中编译得很好,但在 GCC 4.2 (在 Mac 上) 中不能编译。我想知道我是否缺少一些语法魔法。

下面我有一个简化的示例来演示我的错误。对不起,如果这个例子看起来毫无意义,我尽可能多地删除以隔离这个错误。

特别是我有一个模板类 S,它有一个内部类 R,它也是一个模板类。从顶级模板函数 foo,我试图调用 R::append,它是 R: 的静态成员函数:

template< typename C >
struct S {
    template< typename T >
    S<C> & append( const T & ) { return *this; }

    template< int B >
    struct R {
        template< typename N >
        static S<C> & append( S<C> & s, const N ) {
            return s.append( 42 );
        }
    };
};

template< typename C >
S<C> & foo( S<C> & s, const int n ) {
    S<C>::R<16>::append( s, n ); // error: '::append' has not been declared
    return s;
}

有谁知道我做错了什么?

【问题讨论】:

  • +1,我想知道这是 Visual Studio 中的错误还是 gcc 中的错误。
  • 显然这是 Visual C++ 的非标准“功能”。 C++ 规范说在这种情况下需要消除歧义。 VC让你跳过它。我想知道是否有针对 VC 的编译器开关以使其更严格地执行规范合规性?

标签: c++ macos templates gcc


【解决方案1】:

我让它编译:

template< typename C >
S<C> & foo( S<C> & s, const int n ) {
    typedef typename S<C>::template R<16> SR;
    SR::append( s, n );
    return s;
}

【讨论】:

  • typedef 不是必需的,但为了清楚起见,即使我只使用一次,我也更喜欢给复杂类型起别名。
【解决方案2】:

你必须告诉编译器依赖名称R是一个模板:

template< typename C >
S<C> & foo( S<C> & s, const int n ) {
    S<C>::template R<16>::append( s, n );
    return s;
}

【讨论】:

  • 那么你认为VC接受原始代码是不符合标准的?
  • 感谢您的帮助。这确实解决了我在 Mac 上的错误。我也修复了我的实际项目,它现在可以在 Mac 和 PC 上构建。我不确定哪个编译器更符合要求。
  • @Terry:我发布了一个答案来实际解释这件事。我认为 gcc 在这一点上实际上更符合要求,但我可能错了。
【解决方案3】:

同时使用 Visual Studio 和 gcc,这是一个已知问题 :) 我使用 VS2003 和 gcc 3.4.2,所以有一段时间了。

如果我没记错的话,问题是由于模板在这些编译器上的解析方式造成的。

gcc 的行为符合标准规定,并执行 2 次解析:

  • 第一次遇到模板时,没有任何关于类型的信息,此时需要一些 typenametemplate 魔术来帮助了解发生了什么
  • 实际实例化具有给定类型的模板时需要一秒钟

另一方面,VS 只在实例化时进行一次解析,因此可以完全解析没有typenametemplate 的符号。

方法也一样:

template <class Item>
struct Test
{
  template <class Predicate>
  void apply(Predicate pred);

  void doSomething { this->apply(MyPredicate()); }          // Visual Studio
  void doSomething { this->template apply(MyPredicate()); } // gcc
}; // struct Test

在同一个主题上,如果你这样做:

template <class Item>
struct Test { static const std::string Name; };

您需要为模板的每个实例化实际定义此static 属性,否则您将有一个未定义的符号。

VS 接受这种语法:

const std::string Test<MyType>::Name = "MyType";

但是 gcc 需要一个小关键字:

template <> const std::string Test<MyType>::Name = "MyType";

你可能认为 VS 更好,因为它对你的要求更少,但另一方面 gcc 可能会在第一次解析模板方法/类时警告你错误(即没有任何实际实例化)就个人而言,越早越好。

显然,好消息是如果在 gcc 上编译(针对这些问题),它在 Visual Studio 上也能正常编译。

由于我不是标准主义者,我不确定标准是否实际要求或建议 2-parses 方案。

【讨论】:

  • 该标准确实要求对非依赖名称和依赖名称进行两阶段查找。
  • 有人有胶水,哪个 MSVC 版本消除了这种晦涩难懂并实现了标准?显然,代码可能需要有故障的宏才能保持可移植性,不仅可以跨平台,还可以跨 MSVC 版本。
【解决方案4】:

尝试在 struct R::append 中写入“const int N”,然后使用 N(而不是 42?)。

【讨论】:

    猜你喜欢
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多