【问题标题】:Invalid template instantation and the metaprogram compiles fine?无效的模板实例化和元程序编译正常?
【发布时间】:2014-06-08 13:11:57
【问题描述】:

我正在为常见的“条件错误类型”问题(例如 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>;

所以我想到的解决方案是这样包装条件的两个目标,并实例化条件的结果,所以只有选定的模板被实例化。为此,我修改了lazyimpl::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&lt;bool&gt; 特化!

元程序还有一个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&lt;bool&gt;ok 的显式实例化,这应该是该实例的别名。
最后我检查了没有foo&lt;bool&gt;没有匹配的特化”,直到我声明了一个ok类型的变量:ok anok;

我正在使用 C++11 模式 (-std=c++11) 的 CLang 3.4。我的问题是:这里发生了什么?这种行为是正确的还是 LLVM 错误?

编辑: Here 是在 ideone 运行的 SSCCE。它使用 GCC 4.8.1,但似乎具有相同的行为。

【问题讨论】:

  • 只有在需要完整的对象类型时才会进行实例化。只需命名foo&lt;bool&gt; 不需要实例化。见 [temp.inst]/1
  • @dyp 这就是我的想法。但是对其进行 typedef 并不会实例化模板?
  • typedef 是否需要完整的类型? typedef void my_void;
  • @dyp mmm 好的。谢谢你的帮助。所以我误解了实例化规则
  • std::conditional 的问题是过早地评估用作参数的元函数:std::conditional&lt;b, fun0&lt;arg&gt;::type, fun1&lt;arg&gt;::type&gt;::type 而不是,比如说,std::conditional&lt;b, fun0&lt;arg&gt;, fun1&lt;arg&gt;&gt;::type::type

标签: c++ templates c++11 template-meta-programming llvm-clang


【解决方案1】:

说来话长;短。

您实际上并没有在以下表达式中实例化foo&lt;bool&gt;

std::is_same<ok, foo<bool>>::value;

隐式实例化什么时候发生?

14.7.1 隐式实例化 [templ.inst]

7)如果需要一个类模板特化的隐式实例化并且模板被声明但没有被定义,那么程序是非良构的。

上面的文字说的是,类模板只有在需要完全定义的上下文中使用时才会隐式实例化,例如在声明所述模板的对象时,或者在尝试访问其中的某些内容时.

检查一种类型是否与另一种类型相同不能算作这样的上下文,因为我们只是比较两个名称


什么时候需要完全定义的对象?

标准在几个不同的地方引用了“完全定义”,主要是在它明确表示需要这样的对象时。

当我们需要一个完全定义的对象时,最简单的定义是阅读以下内容,它解释了它不是什么。

3.9p5 类型 [basic.types]

已声明但未定义的类、大小未知或元素类型不完整的数组是未完整定义的对象类型。未完全定义的对象类型和 void 类型是不完全类型 (3.9.1)。对象不应被定义为具有不完整的类型。

上面的措辞表明,只要我们不将对象声明为不完整类型,我们就清楚了; IE。我们的模板不会被隐式实例化。

参见下面的示例,其中 (C) 和 (D) 尝试创建 incomplete-type 的对象,两者 (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>`

【讨论】:

    猜你喜欢
    • 2021-08-21
    • 1970-01-01
    • 2017-06-19
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    相关资源
    最近更新 更多