'if'是否有模板参数或SFINAE是首选?

时间:2013-04-23 20:37:41

标签: c++ templates c++11 sfinae

首选是:

template<typename T>
bool isNotZero(const T &a)
{
    if (std::is_floating_point<T>::value) return abs(a) > std::numeric_limits<T>::epsilon();
    else return a;
}

或者这个:?

template<typename T>
std::enable_if<std::is_floating_point<T>::value, bool>::type
isNotZero(const T &a) { return abs(a) > std::numeric_limits<T>::epsilon(); }

template<typename T>
std::enable_if<std::is_integral<T>::value, bool>::type
isNotZero(const T &a) { return a; }

我通常使用第一种类型来避免许多版本的函数。

我相信它完全一样。

第一个版本在操作码阶段优化,第二个版本在模板实例化阶段优化。

2 个答案:

答案 0 :(得分:19)

  

我相信它完全一样。

我不会说完全相同。

在第一个版本中,您使用的是在运行时评估的条件语句,但决定执行哪个分支的条件可以(并且)决定 at编译时

因此,无论输入的类型是什么,你的两个分支都必须编译并编译,即使我们在编译时知道其中只有一个将被执行而另一个将死 - 我期望编译器会在这里发出警告。

在第二种情况下,您只需编译(并执行当然)适合输入类型的内容。在我看来,这使得第二种方法更加出色。

现在即使在这种特殊情况下,你可能选择哪种方法没有区别,由编译时条件决定的条件执行应该通过编译时结构表示 - SFINAE和模板重载,而{{ 1}}应该用于依赖于系统运行时状态的条件。

第一种方法是不可能的,例如,如果条件包含代码的两个分支仅在执行相应分支时编译。请考虑以下两种类型:

if

以下功能模板:

struct X
{
    X(int) { }
};

struct Y
{
    Y() { }
};

现在以下调用都不合法:

template<typename T>
T foo(const T &a)
{
    if (std::is_constructible<T, int>::value)
    {
        return T(42);
    }
    else
    {
        return T();
    }
}

仅此一点表明,处理编译时条件执行的适当工具是模板重载+ SFINAE (或可能涉及类模板特化的等效构造)。

当然,有退化案例(例如此案例)允许您使用其他工具,但如果我们正在寻找概念上正确的设计指南,我相信这里有一个明显的赢家。< / p>

如果像C ++这样的static if之类的东西当然会有所不同,但目前情况并非如此 - 而且即使在不久的将来也不会如此。

答案 1 :(得分:4)

目前,我更愿意使用SFINAE。使用SFINAE不需要任何优化,因为您明确允许根据情况仅调用其中一个函数。没有优化只是因为在运行时调用适当的函数而不做决定。

使用if条件语句将责任放在程序上以便在运行时做出决定。当然,这个可以被优化掉,但可能不是。无论为特定模板参数实际执行哪一个,都可能导致两个分支的编译。这意味着每个分支中的代码必须在语法和语义上对任何给定的模板参数都是正确的。

也许有一天我们会有一个static if,它允许你将if语句写成编译时条件,但目前有一些strong feelings about this:< / p>

  

最近针对C ++提出的static if功能从根本上说是好的,   它的采用将成为语言的灾难。

然而,在不久的将来(旨在围绕C ++ 14发布)我们可能有constraints(a.k.a. concepts-lite),它可以让你像这样编写函数:

template<Floating_point T>
bool isNotZero(const T &a) { return abs(a) > std::numeric_limits<T>::epsilon(); }

template<Integral T>
bool isNotZero(const T &a) { return a; }

这相当于写作:

template<typename T>
  requires Floating_point<T>()
bool isNotZero(const T &a) { return abs(a) > std::numeric_limits<T>::epsilon(); }

template<typename T>
  requires Integral<T>()
bool isNotZero(const T &a) { return a; }

Floating_pointIntegral约束只是constexpr谓词的模板参数,这些参数在编译时检查并参与重载解析。这将是编写这样一组函数的首选方法。