std :: conjunction中的短路如何工作

时间:2018-11-18 01:39:27

标签: c++ templates language-lawyer metaprogramming

给出以下代码(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<Bn...>会如何被语言所取代?

2 个答案:

答案 0 :(得分:4)

cppreference(您从中实现的页面)提供了explanation的工作方式:

  

连接是短路的:如果存在带有Bi的模板类型参数bool(Bi::value) == false,则实例化conjunction<B1, ..., BN>::value不需要为{{1 }}。

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

Bj::value

等效于:

j > i

应该引起实例化的东西,如下所示:

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

答案 1 :(得分: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<false, int, HardError<int>>的实例化不会导致HardError<int>的实例化

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

以下代码也有效:

struct incomplete_tag;

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