【问题标题】:std::conditional compile-time branch evaluationstd::条件编译时分支评估
【发布时间】:2014-07-28 16:40:26
【问题描述】:

编译:

template < class T, class Y, class ...Args >
struct isSame
{
    static constexpr bool value = std::conditional<
                                      sizeof...( Args ),
                                      typename std::conditional<
                                          std::is_same< T, Y >::value,
                                          isSame< Y, Args... >, // Error!
                                          std::false_type >::type,
                                      std::is_same< T, Y > >::type::value;
};

int main()
{
    qDebug() << isSame< double, int >::value;
    return EXIT_SUCCESS;
}

给我这个编译器错误:

error: wrong number of template arguments (1, should be 2 or more)

问题是isSame&lt; double, int &gt; 有一个空的Args 参数包,所以isSame&lt; Y, Args... &gt; 实际上变成了与签名不匹配的isSame&lt; Y &gt;

但我的问题是:为什么要评估那个分支? sizeof...( Args )false,因此不应评估内部 std:conditional。这不是运行时代码,编译器知道sizeof..( Args )永远在给定模板类型下是true

如果你好奇,它应该是std::is_same 的可变参数版本,而不是它的工作原理......

【问题讨论】:

标签: c++ templates c++11 conditional


【解决方案1】:

您有一个错误,因为在用作模板参数时类型必须正确。
您可以使用模板专业化来解决您的问题,例如:

#include <type_traits>

template <typename ... Ts> struct are_same;

template <> struct are_same<> : std::true_type {};
template <typename T> struct are_same<T> : std::true_type {};

template <typename T1, typename T2, typename... Ts>
struct are_same<T1, T2, Ts...> :
    std::conditional<
        std::is_same<T1, T2>::value,
        are_same<T2, Ts...>,
        std::false_type
    >::type
{};

static_assert(are_same<char, char, char>::value, "all type should be identical");
static_assert(!are_same<char, char, int>::value, "all type should not be identical");

【讨论】:

  • 我希望避免模板专业化,因为它很冗长,但事后看来,它比我想出的更具可读性(而且它有效......)。
【解决方案2】:

问题是条件只是一个模板。它的类型取决于所有 3 个参数,即使 ::type 字段仅取决于其中的 2 个(bool 和它选择的任何一个)。

要做你想做的事,传递代表如何将参数应用到双方的类型。m 然后将参数传递给结果。

template<template<class...>class Target>struct defer{
  template<class...Ts>using execute=Target<Ts...>;
};
template<class T>struct sink{
  template<class...>using execute=T;
};
template< class Prog, class... Ts > using run=Prog::template execute<Ts...>;

using choice = typename std::conditional< test, defer<Foo>, sink< std::false_type >  >::type;
using result = run< choice, int, double >;

给我们一个返回 deferd Foosinkd false_type 的条件。然后我们可以使用一些参数运行它,给我们Foo&lt;int, double&gt;false_type

sinkexecute 忽略参数并始终返回传入的类型,而defer 将它们应用于传入的template

这里的诀窍是如果test 为假,则Foo&lt;int, double&gt; 不会运行,因为在这种情况下choicesink&lt;false_type&gt;

template<bool b, class A, class B, class...Ts>
using pick = run< typename std::conditional< b, A, B >::type, Ts... >;

using result=pick<test, defer<Foo>, sink<std::false_type>, int, double >;

在一行中与上面的choice/result 相同。

为了解决您的问题,我们推迟在您的一个 if 分支上执行:

static constexpr bool value =
typename std::conditional< sizeof...( Args ),
  pick< std::is_same< T, Y >::value,
    defer<isSame>,
    sink<std::false_type>,
    Y, Args...
  >,
  std::is_same< T, Y >
>::type::value;

现在,通常有一些方法可以避免这种技术,但你应该知道它存在。

【讨论】:

  • Boost.MPL 有 eval_ifidentity 用于延迟实例化
  • @templaterex 当然,但是在 C++11 中编写自己的元程序如此简单时,为什么要使用 boost?回到 03 年,那是一种痛苦……
  • 当然,但是在简单的事情之后,您会想要使用通用的编译时算法,然后 Boost.MPL 仍然具有优势,您自己的助手可能无法与它互操作。 IMO 仍然非常需要 C++14 风格的 MPL。
【解决方案3】:

别怪std::conditional

简而言之,这是因为模板参数替换发生在std::conditional之外,这意味着导致错误的不是std::conditional。它是isSame

想象std::conditional 是一个函数调用(它不是),我们作为参数传递的内容被评估函数的实际主体被评估之前,我们作为参数传递的内容确定必须是一个有效的构造,即使函数本身不使用它们。

以下链接答案中提供了进一步阅读:


解决方案

添加一些间接性,这样你就不会实例化isSame 如果sizeof... (Args) == 0,你可以有一个用作isSame_if_not_empty&lt;Args..&gt;::type 的特征,如果Args 不为空,它将产生isSame&lt;Args...&gt;,如果它不为空,则为其他东西确实是空的。


建议的解决方案

修复,使isSame 可以与空的可变参数包一起使用,产生 true,这是明智的方法。如果isSame 的描述是“所有传递的类型都是相同的类型”,那么一个空包肯定具有相同类型的“所有类型”。

【讨论】:

    【解决方案4】:

    但我的问题是:为什么要评估那个分支?

    因为没有评估,那根本就不是一个分支。 这是一个模板及其实例,以成功实例化 std::conditional 模板
    我的意思是,评估抽象在编写模板元程序时很有用,但你永远不应该忘记什么模板系统真的在做

    如果您需要有条件地实例化一个可能格式错误的模板,请添加一个间接级别。查看this 答案以获取相关示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-27
      • 2015-10-04
      • 2012-02-01
      • 2015-01-19
      • 1970-01-01
      • 2016-11-12
      • 2011-10-10
      • 2020-10-01
      相关资源
      最近更新 更多