std ::条件编译时分支评估

时间:2014-06-07 14:35:18

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

编译:

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< double, int >有一个空的Args参数包,因此isSame< Y, Args... >实际上变为isSame< Y >,与签名不匹配。

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

如果您有点好奇,它应该是std::is_same的可变版本,而不是它有效...

4 个答案:

答案 0 :(得分:4)

  

但我的问题是:为什么要对该分支进行评估?

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

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

答案 1 :(得分:3)

不要责怪std::conditional

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

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

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


解决方案

添加一些间接,以便在isSame时不实例化sizeof... (Args) == 0,如果isSame_if_not_empty<Args..>::type,您可以使用isSame<Args...>作为Args。如果它确实是空的,那么它不是空的,而是其他的东西。


提议的解决方案

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

答案 2 :(得分:3)

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

#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");

答案 3 :(得分:3)

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

要执行您想要的操作,请传递表示如何将参数应用于双方的类型。然后将参数传递给结果。

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 >;

为我们提供了一个返回defer d Foosink d false_type的条件。然后我们可以使用一些参数运行它,为我们提供Foo<int, double>false_type

sink的{​​{1}}忽略参数并始终返回传入的类型,而execute将它们应用于传入的defer

这里的诀窍是,如果template为false,则Foo<int, double>不会运行,因为在这种情况下testchoice

sink<false_type>

与上面的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相同。

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

result

现在,通常有办法避免这种技术,但你应该知道它存在。