我正在研究一个简单的解决方案,以解决常见的“条件错误的类型”(就像昨天的this问题)。
在我的代码库中,我有一个模板来保存未实例化的模板,并在以后实例化它们。像这样:
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
也采用模板参数,但它们将被忽略。我以这种方式使用了donde,以使两个用例具有相同的界面。因此,我们的主要问题是对潜在病态类型的条件选择进行评估的方法如下:
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>
{};
似乎可行,不是吗?大。但是几分钟后,我将 bool(boolean) 标志更改为
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;
我正在C++ 11模式(
-std=c++11
)上使用CLang 3.4。我的问题是:这是怎么回事?这是正确的行为还是LLVM错误? 编辑: Here是在ideone上运行的SSCCE。它使用GCC 4.8.1,但似乎具有相同的行为。
最佳答案
很长的故事;短。
您实际上不是在以下表达式中实例化foo<bool>
:
std::is_same<ok, foo<bool>>::value;
隐式实例化何时发生?
上面的文字说的是,类模板仅在需要完全定义的上下文中使用时才隐式实例化,例如在声明所述模板的对象时或尝试访问其中的某些内容时。
因为我们只是比较两个名称,所以检查一种类型是否与另一种类型相同并不视为这种上下文。
什么时候需要一个完全定义的对象?
该标准在几个不同的位置引用了“完全定义的”,主要是当它明确表示需要这样的对象时。
当我们需要一个完全定义的对象时,最简单的定义是阅读以下内容,这说明了不需要什么。
上面的措辞指出,只要我们不声明对象是不完整类型,我们就很清楚。即。我们的模板不会被隐式实例化。
请参见下面的示例,其中(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>`