【问题标题】:How does short-circuiting work in std::conjunction [duplicate]短路如何在 std::conjunction 中工作 [重复]
【发布时间】:2018-11-18 01:39:26
【问题描述】:

给定以下代码 (https://wandbox.org/permlink/Eof3RQs49weJMWan)

#include <tuple>
#include <type_traits>
#include <utility>

template <typename T>
inline constexpr auto always_false = false;

template <typename T>
class HardError {
    static_assert(always_false<T>);
};

int main() {
    std::ignore = std::conjunction<std::false_type, HardError<int>>{};
}

我试图理解为什么上面使用的std::conjunction 不会出错。我知道这是为了允许短路,因此不会发生这种情况,这是设计使然。

但是,我不明白允许这种情况发生的语言规则。鉴于下面std::conjunction的实现

template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...> 
    : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};

我们最终继承了 std::conditional 的这种特化

template<class T, class F>
struct conditional<false, T, F> { typedef F type; };

这需要两种类类型进行实例化。那么conjunction&lt;Bn...&gt;是如何被语言省略的呢?

【问题讨论】:

  • 现在,将typedef HardError&lt;int&gt; zz; 添加到您的示例代码中。这也将编译没有任何错误。现在,尝试实例化zz,只有然后会出现错误。仅仅因为引用了具有失败的静态断言的类不会触发静态断言,但是当您尝试创建该类的实例时。在解开std::conjunction 期间没有实例化HardError
  • 另外,根据[temp.res]/8.1,我认为是格式不正确,不需要诊断

标签: c++ templates language-lawyer metaprogramming


【解决方案1】:

cppreference(您从中提取该实现的页面)提供了一个 explanation 来说明其工作原理:

连接是短路的:如果有一个模板类型参数Bibool(Bi::value) == false,那么实例化conjunction&lt;B1, ..., BN&gt;::value 不需要为j &gt; i 实例化Bj::value

具体来说,我们可以看到这一点。您在 main 中的行:

std::ignore = std::conjunction<std::false_type, HardError<int>>{};

相当于:

std::ignore.operator=(conjunction<std::integral_constant<bool, 0>, HardError<int> >{{}});

这应该会导致实例化,如下所示:

template<>
struct conjunction<std::integral_constant<bool, 0>, HardError<int> > : public std::integral_constant<bool, 0>
{
  inline ~conjunction() noexcept = default;

};

【讨论】:

  • 欢迎来到 StackOverflow :) 我知道编译器会创建类似这样的实例化。我正在尝试了解是什么语言规则导致这种格式不正确。
【解决方案2】:

模板的实例化不会触发其模板参数的实例化。见[temp.inst/2]

这是延迟硬错误的典型方式。

template<class T> struct delay {
    using run = T;
};

int main() {
    // force instantiation of delay<HardError<int>>,
    // but HardError<int> itself is not instantiated
    sizeof(delay<HardError<int>>);

    delay<HardError<int>> a; // OK, same as above

    // Now HardError<int> is instantiated, static_assert failure
    // sizeof(delay<HardError<int>>::run);
}

出于同样的原因,std::conditional&lt;false, int, HardError&lt;int&gt;&gt; 的实例化不会导致HardError&lt;int&gt; 的实例化

此外,模板参数甚至不需要完整。

以下代码也有效:

struct incomplete_tag;

int main() { sizeof(delay<incomplete_tag>); }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 1970-01-01
    • 2011-04-07
    • 2020-05-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    相关资源
    最近更新 更多