2010-01-19 45 views
4

我有一些在VC9(Microsoft Visual C++ 2008)中编译好的模板代码,但不能在GCC 4.2(在Mac上)编译。我想知道是否有一些我错过的语法魔法。调用模板化内部类静态成员函数的C++语法?

下面我有一个精简的例子,它演示了我的错误。对不起,如果这个例子似乎毫无意义,我尽可能地去除这个错误。

特别是我有一个模板类S,它有一个内部类R也是一个模板类。从顶级模板函数foo,我试图调用[R ::追加其为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; 
} 

任何人在那里知道我做错了吗?

+0

+1,我想知道这是Visual Studio中的错误还是gcc中的错误。 – 2010-01-19 07:57:13

+0

显然这是Visual C++的一个非标准“特性”。 C++规范指出在这种情况下需要消歧。 VC让你跳过它。我不知道是否有一个编译器开关让VC更严格地执行规范合规性? – jwfearn 2010-01-21 01:45:22

回答

2

有使用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分析方案。

+0

该标准确实要求对非依赖名称和从属名称进行两阶段查找。 – ephemient 2010-01-19 19:18:14

+0

有人有一个胶水,MSVC版本降低了这个默默无闻,并实现标准?很明显,代码可能需要不稳定的宏来保持不仅跨平台,而且跨越MSVC版本。 – lef 2014-11-24 23:40:19

0

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

4

我得到它的编译:

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; 
} 
+0

typedef不是必需的,但我也喜欢将复杂类型别名,即使我只使用它们一次,只是为了清楚起见。 – 2010-01-19 10:00:37

4

你必须告诉编译器将从属名称R是一个模板:

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

所以你认为VC通过接受原始代码是不符合的? – 2010-01-19 08:02:36

+0

感谢您的帮助。这确实修复了我在Mac上的错误。我修复了我的实际项目,并且现在可以在Mac和PC上构建。我不确定哪个编译器更符合要求。 – jwfearn 2010-01-19 08:18:59

+0

@Terry:我发布了一个实际解释事情的答案。我认为gcc实际上更符合这一点,但我可能是错的。 – 2010-01-19 10:16:37