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

标签 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;
}

Error 1 error C2039: '{ctor}' : is not a member of '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;
}

信用到期的信用,这是Dietmar Kühl给我的稍微修改的版本)

最佳答案

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

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

此声明将模板 Foo<> 的第二个模板参数默认为第一次声明。所以明确地它的类型是:

/*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 就不会存在了,我们的编译器就会吐出来。

好的,我们知道如果 /*4*/将完全编译,然后 some_boolean_consant == true ,以及导出类型 typeenable_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'

或大意的词。

会合
/*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 */ 级别的修复程序当我们知道错误不是
一般在那个级别。它在编译器的模板元编程工作中默默无闻,所以
不痒的解决方法至少仅限于 namespace cool .

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

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

例如,类(class) /*3*/其神秘的毒性 ||手术。 namespace cool 的 TMP 助手类做他们的
bool 常量的元逻辑 - 不必要。

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<> 的相应声明如下所示:
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;

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

关于c++ - 以下模板特化代码是非标准的还是 VS-C++ 中的错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9669844/

相关文章:

c++ - 返回值类型与函数类型 CONST 不匹配

c++ - 如何使派生类在 CRTP 中的基类上模板化

visual-c++ - MSVC++ map<int, list<unique_ptr<test>>> 不编译引用已删除的函数

c++ - 如何使用具有多个比较器功能的 std::multiset?

c++ - 如何验证另一个程序的窗口是否最小化?

c++ - 搜索 C++ 字符串并剥离文本(如果存在)

c++ - 在无尽的跑酷游戏中处理大的 x 值

c++ - 在映射中存储指向成员函数的指针

c++ - 在 C++ 中,模板在我专门化时是否实例化了?

c++ - 基类未定义 Visual Studio 2010