下面的模板专门化代码是非标准的,或者是vs - c++中的错误

Is the following template specialization code non-standard or a bug in VS-C++?

本文关键字:vs 或者是 错误 c++ 专门化 代码 非标准      更新时间:2023-10-16

下面的代码可以在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;
}

Credit where Credit due,这是Dietmar khl给我的一个稍微修改的版本

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>

那么你可以在集合地点再见。

我们可以看到类型:

/*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<>应该导出一个typedeftype,它具有第二个模板的类型参数。如果第一个bool形参是false,那么该类型定义将不存在,编译器将关闭。

好了,我们知道如果/*4*/可以编译,那么some_boolean_consant == true,并且导出的类型type是ofenable_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() {
}

则程序用visualc++编译干净。

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::逻辑被打破了)。

<标题>

解决方案?我们已经看到了一个。只要在Foo<Policy>的定义中明确void模板参数即可和Foo<Folicy>::Foo()。如果你只想知道这些,现在就可以走了。

但是那个很痒。当我们知道这个bug不存在时,我们正在/* Simple Case */级别应用修复那个级别的将军。它是在编译器的模板元编程工作的模糊,所以不痒的解决方案至少仅限于namespace cool

这里有一个关于模板元编程(TMP)助手模板的警告规则,我希望它的进展编译器很快就会让我忘记:不要让blighters在飞行中实例化。这样的模板只存在以方便编译时逻辑。只要编译器可以通过递归定义来执行该逻辑类型很可能会保持在它的舒适区(至少只要它的实例化深度保持不变)。如果它必须实例化中间类型,以促进编译时逻辑,这是最奇怪的小过失很容易浮出水面。

如果您以这样一种方式编写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<>的声明如下:

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::的东西都不会被实例化,直到整个系统被实例化实例化:我们只处理所有元逻辑的类型。如果这些变化对程序进行修改,则不需要令人发痒的变通方法。而且GCC也很满意。

这似乎与编译器的这个错误有关。

哦。应该是:

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

(在结构声明中没有<Policy>)不确定标准是怎么说的,但是在结构名后面带template形参的模板结构体声明应该是声明该模板的部分专门化。