无效的模板实例化和元程序编译良好
Invalid template instantation and the metaprogram compiles fine?
我正在研究一个简单的解决方案来解决常见的"病态类型的条件"问题(就像昨天的问题)。
在我的代码库中,我有一个模板来保存未实例化的模板,并在以后实例化它们。像这样:template<template<typename...> class F>
struct lazy
{};
namespace impl
{
template<typename L , typename... ARGS>
struct lazy_instance;
template<template<typename...> class F , typename... ARGS>
struct lazy_instance<lazy<F>,ARGS...> : public identity<F<ARGS...>>
{};
}
template<typename L , typename... ARGS>
using lazy_instance = typename impl::lazy_instance<L,ARGS...>::type;
其中identity
为恒等元函数。
可以这样使用:
using vector = lazy<std::vector>;
using int_vector = lazy_instance<vector,int>;
因此,我想到的解决方案是以这种方式包装条件的两个目标,并实例化条件的结果,因此只有选定的模板被实例化。为此,我修改了lazy
和impl::lazy_instance
,以允许我们在lazy
实例点传递模板参数:
template<template<typename...> class F , typename... ARGS>
struct lazy
{};
namespace impl
{
template<typename L , typename... ARGS>
struct lazy_instance;
template<template<typename...> class F , typename... ARGS , typename... IARGS>
struct lazy_instance<lazy<F,ARGS...>,IARGS...> : public identity<F<ARGS...>>
{};
}
注意,在这种情况下,lazy_instance
也接受模板参数,但是它们被忽略了。我这样做是为了在两个用例中使用相同的界面。
所以我们的主要问题,潜在病态类型的条件选择的评估可以通过以下方式实现:
using ok = lazy_instance<typename std::conditional<true,
lazy<foo,int>,
lazy<foo,bool>
>::type
>;
其中foo
是模板,其中bool
实例是不正确的,例如:
template<typename T>
struct foo;
template<>
struct foo<int>
{};
它似乎有效,不是吗?太好了。但是几分钟后,我把布尔标志改为false
,令人惊讶的是它也起作用了!即使没有定义foo<bool>
专门化!
元程序下面也有一个static_assert
来检查条件的求值是否成功:
static_assert( std::is_same<ok,foo<int>>::value , "Mmmmm..." );
当我将条件从true
更改为false
时,我更改了测试以查看发生了什么:
static_assert( std::is_same<ok,foo<bool>>::value , "Mmmmm..." );
并且元程序也通过了测试!即使foo<bool>
和ok
的显式实例化,也应该是该实例的别名。
最后,我检查了没有"foo<bool>
没有匹配的专门化",直到我声明类型为ok
的变量:ok anok;
我正在使用CLang 3.4,在c++ 11模式(-std=c++11
)上工作。我的问题是:这里发生了什么?这种行为是正确的还是LLVM错误?
EDIT:这是一个运行在ideone上的SSCCE。它使用GCC 4.8.1,但似乎具有相同的行为
说来话长;短。
您实际上没有在下面的表达式中实例化foo<bool>
:
std::is_same<ok, foo<bool>>::value;
隐式实例化何时发生?
14.7.1
隐含实例化[templ.inst]
5)
如果类类型在需要完全定义的对象类型的上下文中使用,或者类类型的完整性可能影响程序的语义,则隐式实例化类模板专门化。
7)
如果需要类模板专门化的隐式实例化,并且声明了模板但没有定义模板,则程序是病态的。
上面的文本说的是,类模板只有在需要完全定义的上下文中使用时才会隐式实例化,例如当声明所述模板的对象时,或者当试图访问其中的某些内容时。
检查一个类型是否与另一个类型相同不算作这样的上下文,因为我们只是比较两个名称。
何时需要完全定义的对象?
标准在几个不同的地方引用了"完全定义的",主要是当它明确地说需要这样一个对象时。
何时需要完全定义的对象,最简单的定义是阅读下面的内容,它解释了它不是什么。
上面的措辞表明,只要我们不声明一个对象为不完整类型,我们就没有问题;ie。我们的模板不会被隐式实例化。
3.9p5
Types[basic.types]
已声明但未定义的类、未知大小的数组或元素类型不完整的数组是未完全定义的对象类型。不完全定义的对象类型和void类型是不完全类型(3.9.1)。对象不能定义为不完整类型。
参见下面的例子,其中(C)和(D)试图创建一个不完整类型的对象, (A)和(B)都是合法的,因为它们不会导致隐式实例化。
template<class T> struct A;
typedef A<int> A_int; // (A), legal
A<int> * ptr; // (B), legal
A<int> foo; // (C), ill-formed; trying to declare an object of incomplete-type
A<int>::type baz; // (D), ill-formed; trying to reach into the definition of `A<int>`
- 这是使用回溯的 nqueen 问题,但我使用了动态 2d 数组,我的程序编译良好,但不返回任何输出
- C ++程序编译错误,找不到/访问文件
- C++程序编译没有问题,但无法运行
- Mongdb C++ 驱动程序编译错误 for document{}.
- 添加类型名会导致程序编译失败
- 将 C 程序更改为 C++ 程序 - 编译错误
- 程序编译,但当分解为函数时实际上不会移动电机
- 如何链接 glib-2.0 库进行 c/c++ 程序编译
- 为什么 gcc 和 clang 为我的程序编译为不同的"const"结果?
- 复数程序编译时的计算方法错误
- qt 命令行应用程序编译
- 程序编译和运行,但一段时间后,它停止使用Typedef关键字在CPP上工作
- QML应用程序编译一个 *form.ui.qml文件,但忽略了关联的.qml文件
- 为什么该程序编译但不显示任何输出
- 分割故障程序编译为共享库,但不静态
- 程序编译,但我认为开关被忽略
- 程序编译后崩溃
- 什么是程序编译中的PIC级别
- 尽管使用不存在的成员,但程序编译
- C++程序编译在 Ubuntu 中失败,但在 MacOSX 中工作