【问题标题】:'if' with template parameters or SFINAE is preferred?'if' 带有模板参数或 SFINAE 是首选?
【发布时间】:2013-04-17 05:21:03
【问题描述】:

首选是这样的:

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

我通常使用第一种来避免许多版本的功能。

我相信是完全一样的。

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

【问题讨论】:

  • 我更喜欢前者,因为在这种特殊情况下更容易理解。但是,当函数的主体更复杂时,后者可能很有用。

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


【解决方案1】:

我相信是完全一样的。

我不会说一模一样

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

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

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

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

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

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();
    }
}

现在以下调用都不合法:

foo(X()); // ERROR! X is not default-constructible
foo(Y()); // ERROR! Y is not constructible from an int

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

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

如果 C++ 中存在 static if 之类的东西,情况当然会有所不同,但目前情况并非如此 - 似乎在不久的将来也不会。

【讨论】:

  • 我还要指出,任何来自不兼容类型的编译器错误都会通过 SFINAE 方法更清晰,并且肯定会被捕获,而不是通过隐式转换可能会做错事。
  • @diverscuba23:如果我理解正确你的意思,那么是的,在一般情况下,错误消息会更清楚 - 提到找不到过载,以及替换失败的原因
【解决方案2】:

目前,我更喜欢使用 SFINAE。使用 SFINAE 不需要任何优化,因为您明确允许根据情况仅调用其中一个函数。无需执行优化,因为将在运行时调用适当的函数而无需做出决定。

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

也许有一天我们会有一个static if,它允许你编写if 语句作为编译时条件,但目前有一些strong feelings about this

最近为 C++ 提出的static if 特性存在根本缺陷, 并且它的采用对于该语言来说将是一场灾难。

但是,在不久的将来(目标是围绕 C++14 发布)我们可能会拥有 constraints(又名概念精简版),它允许您编写如下函数:

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 谓词,它们在编译时检查并参与重载决议。这将是编写这样一组函数的首选方式。

【讨论】:

  • 未来注意事项:C++14 没有概念,但 C++17 可能会有。
猜你喜欢
  • 2015-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-15
  • 1970-01-01
相关资源
最近更新 更多