下面的模板专门化代码是非标准的,或者是vs - c++中的错误
Is the following template specialization code non-standard or a bug in VS-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;
}
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形参的模板结构体声明应该是声明该模板的部分专门化。
- 为什么文件不是由 F 流创建的,或者即使它是输出只是垃圾值?
- Visual C++: MSVC vs. GCC+CLANG: 处理 lambda 捕获类成员变量,正确的方法是什么?
- SDL2 不是基于 VS 代码构建的
- 是 VS 代码 MinGW 与 Win32 中未定义的错误
- 这是系统资源吗?(或者我怎么知道我是否需要删除指针) - 在 C++ 中使用 C
- 返回空字符串文字 VS. 返回空点 - 它们是一样的吗?
- 为什么C++数组索引值是有符号的,而不是围绕size_t类型构建的(或者我错了)
- 这个 matlab 代码的C++等价物是什么(fread matlab vs fread C/C++)?
- 调用虚函数的逻辑不清楚(或者是方法隐藏?
- VS Code认为任何文件的第一行都是#include错误
- 需要帮助从12个字节生成新的24字节RGB查找阵列,或者是一种更好的编码方法
- 杀死幻数:"const int" vs "constexpr int"(或者最后没有区别)
- GCC vs Clang 中的堆栈粉碎(可能是由于金丝雀)
- 关键部分或静音是否真的是成员变量,或者何时应成为成员变量
- VS Intellisense显示了某些(不是全部)字节常数的逃脱字符
- 下面的模板专门化代码是非标准的,或者是vs - c++中的错误
- 这怎么可能是竞态条件.或者是我的代码有问题
- 解析是如何工作的,或者是什么使类型完整或不完整
- Is_integral vs is_integer:是冗余的
- 我是否必须破坏孩子的任务,或者是父母的照顾