以下模板专业化代码是非标准的还是VS-C ++中的错误?

时间:2012-03-12 15:22:57

标签: c++ templates visual-c++

以下代码在GCC中编译(我使用的是ideone,它使用gcc-4.3.4)但不在Visual Studio中编译。它是标准代码和Visual C ++ 2008和2010中的错误(我在两者中都尝试过)或非标准错误,GCC很乐意编译它吗?

namespace cool
{
  template <bool, typename = void> struct enable_if {};
  template <typename T> struct enable_if<true, T> { typedef T type; };

  template <typename T0, typename T1> struct is_same { enum { value = false }; };
  template <typename T> struct is_same<T, T> { enum { value = true }; };
}

struct BasePolicy {};
struct BasePolicy2 {};
struct Faz {};

template <typename Policy,
typename = typename cool::enable_if<cool::is_same<BasePolicy, Policy>::value || cool::is_same<BasePolicy2, Policy>::value>::type >
struct Foo;

template <typename Policy>
struct Foo<Policy> {
  Foo();
};

template <typename Policy>
Foo<Policy>::Foo() {
}

int main()
{
  Foo<BasePolicy2> fb;
  // Foo<Faz> fb1;
}
  

错误1错误C2039:'{ctor}':不是'Foo'main.cpp 25的成员

请注意,问题是Foo构造函数的外联定义。如果你在类中定义它,那么Visual-C ++很高兴:

template <typename Policy>
struct Foo<Policy> {
  Foo() {}
};

此外,以下代码在两者上编译(请注意||和缺少后的逻辑):

namespace cool
{
  template <bool, typename = void> struct enable_if {};
  template <typename T> struct enable_if<true, T> { typedef T type; };

  template <typename T0, typename T1> struct is_same { enum { value = false }; };
  template <typename T> struct is_same<T, T> { enum { value = true }; };
}

struct BasePolicy {};
struct BasePolicy2 {};
struct Faz {};

template <typename Policy,
  typename = typename cool::enable_if<cool::is_same<BasePolicy, Policy>::value>::type >
struct Foo;

template <typename Policy>
struct Foo<Policy> {
  Foo();
};

template <typename Policy>
Foo<Policy>::Foo() {
}

int main()
{
  Foo<BasePolicy> fb;
  // Foo<Faz> fb1;
}

信用到期的信用证,这是DietmarKühl给我的略微修改版本

3 个答案:

答案 0 :(得分:1)

Visual C ++ 2008/2010有问题。但它可以解决 - 不止一种方式。

让我们考虑Foo<BasePolicy2> fb的类型。

此声明默认模板Foo&lt;&gt;的第二个模板参数首先声明。所以明确的类型是:

/*1*/ Foo<BasePolicy2,cool::enable_if<
            cool::is_same<BasePolicy, BasePolicy2>::value ||
            cool::is_same<BasePolicy2,BasePolicy2>::value
        >::type
    >

如果您已经对/*1*/归结为:

感到满意
/*2*/ Foo<BasePolicy2,void>

然后你可以在下面的 The Rendezvous 再次见到我们。

好吧,我们可以看到类型:

/*3/ cool::enable_if<
        cool::is_same<BasePolicy, BasePolicy2>::value ||
        cool::is_same<BasePolicy2,BasePolicy2>::value
    >

解析为:

/*4/ cool::enable_if<some_boolean_consant>

接下来,让我们检查template enable_if<>的定义方式。在namespace cool我们得到了:

/*5/ template <bool, typename = void> struct enable_if {};
/*6/ template <typename T> struct enable_if<true, T> { typedef T type; };

因此/*4*/默认为template enable_if<>的第二个模板参数,默认类型为void

好的,然后/*6*/专门针对第二个模板参数template enable_if<>,只要它的第一个bool参数有 值true,并且在这种情况下,enable_if<>将导出具有第二个模板类型的typedef type 参数。如果第一个bool参数是false,那那个typedef就不会存在,我们的编译器将会barf。

好吧,我们知道如果/*4*/完全编译,那么some_boolean_consant == true,导出的类型type就是 enable_if<>的默认第二个模板参数。这是void

现在推断出以下类型:

/*7*/   cool::enable_if<
            cool::is_same<BasePolicy, BasePolicy2>::value ||
            cool::is_same<BasePolicy2,BasePolicy2>::value
        >::type

void。因此/*1*/归结为/*2*/,这是默认类型的Foo<BasePolicy2>

它应该是什么,但是如果你对这个结论有疑问,那么只需将它添加到全局范围的程序中并编译:

typedef cool::enable_if<
            cool::is_same<BasePolicy, BasePolicy2>::value ||
            cool::is_same<BasePolicy2,BasePolicy2>::value
        >::type WhatType;
WhatType what_am_i;

编译器会说:

'what_am_i' : illegal use of type 'void'

或者说是这样的话。

Rendezvous

/*1/ = /*2/的知识使我们对Visual C ++编译错误有所了解,该问题是:

Error 1 error C2039: '{ctor}' : is not a member of 'Foo' main.cpp 25

错误是抱怨产生它的构造函数实际上不是由它必须属于的类型声明的构造函数,即 Foo<Policy>::Foo()不是Foo<Policy>的构造函数。

现在Foo<Policy>的定义默认其初始声明的第二个模板参数,我们知道它必须是void。所以 问题出现了:编译器实际上是否尊重默认的第二个模板参数? - 即它是否认出了 Foo<Policy>的模板定义是Foo<Policy,void>的模板定义?

答案是否定。如果我们只是更改模板及其构造函数的定义以显式指定默认的第二个参数:

template <typename Policy>
struct Foo<Policy,void> {
    Foo();
};

template <typename Policy>
Foo<Policy,void>::Foo() {
}

然后程序用Visual C ++编译清理。

Visual C ++是否可能通过引发此错误而坚持自己 - 有争议的 - 关于标准C ++的信念?还是只是坏了? 它刚好被打破了。因为如果我们在这个更简单但基本上相同的程序上尝试它,它就没有问题:

/* Simple Case */

template<typename X, typename Y = void>
struct A;

template<typename X>
struct A<X> {
    A();
};

template<typename X>
A<X>::A(){};

int main()
{
    A<int> aint;
    return 0;
}

这表明它导致消化不良的模板元编程/*3*/满口, 并且正如提问者指出的那样,如果通过删除||操作来实验性地简化那一口, 一切都很顺利(当然,除了我们的cool::逻辑被打破)。

解决方法?

我们已经看过一个。只需在void的定义中明确Foo<Policy>模板参数 和Foo<Folicy>::Foo()。如果你想知道的话,你现在可以离开。

但那一个痒。当我们知道错误不是时,我们会在/* Simple Case */级别应用此修复程序 一般在那个级别。它隐藏在编译器的模板元编程工作的默默无闻中,所以a 没有痒的解决方法至少会局限于namespace cool

这是一个关于模板元编程(TMP)帮助模板的谨慎规则,我希望其进展 编译器很快就会让我忘记:不要让blighter在飞行中实例化。此类模板仅存在 促进编译时逻辑。只要编译器可以通过递归定义来执行该逻辑  类型很可能会保持在舒适区(至少只要它的实例化深度保持不变)。如果 它必须在编译时逻辑的进一步实例化中间类型,然后才是最奇怪的 peccadilloes易于浮出水面。

如果您以这样的方式编写TMP帮助程序类,那么只能通过获取值来完成编译时逻辑 他们公开的静态或枚举常量,然后你强迫编译器一直实例化, 因为这样只有那些静态或枚举的常量才能获得值。

具有神秘毒性/*3*/操作的||类。 namespace cool的TMP助手类做了他们的 布尔常量的元逻辑 - 不必要的。

对TMP帮助程序类的谨慎方法是定义它们,以便可以模拟逻辑操作,无需实例化, 通过递归类型定义,导出常量 - 以免最终需要它们 - 仅在所有编译时 逻辑成功登陆。谨慎重写namespace cool的内容可能看起来像这样:

namespace cool
{
    template<bool val>
    struct truth_type
    {
        static const bool value = false;
    };

    template<>
    struct truth_type<true>
    {
        static const bool value = true;
    };

    typedef truth_type<true> true_type;
    typedef truth_type<false> false_type; 

    template<class lhs,class rhs>
    struct or_type
    {
        typedef false_type type;
    };

    template<class lhs>
    struct or_type<lhs,true_type>
    {
        typedef true_type type;
    };

    template<class rhs>
    struct or_type<true_type,rhs>
    {
        typedef true_type type;
    };

    template <typename T, typename = void> struct enable_if {};
    template <typename T> struct enable_if<true_type, T> { typedef T type; };

    template <typename T0, typename T1> struct is_same {
        typedef false_type type;
    };
    template <typename T> struct is_same<T, T> {
        typedef true_type type;
    };
}

和模板Foo&lt;&gt;的相应声明看起来像这样:

template <typename Policy,
typename = typename cool::enable_if<
    typename cool::or_type<
        typename cool::is_same<BasePolicy, Policy>::type,
        typename cool::is_same<BasePolicy2, Policy>::type
    >::type
>::type>
struct Foo;

这样,在整个shebang之前,我们的cool::事物都没有被实例化 实例化:我们只处理所有元逻辑的类型。如果这些变化 如果是程序,则不需要发痒的解决方法。 GCC也很满意。

答案 1 :(得分:0)

在我看来,这与编译器的bug有关。

答案 2 :(得分:0)

喔。不应该是:

template <typename Policy>
struct Foo {
  Foo();
};

template <typename Policy>
Foo<Policy>::Foo() {
}

(在结构声明中没有<Policy>。)不确定标准是什么,但在结构名称之后用模板参数声明模板结构应该声明该模板的部分特化。