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

Cha*_*eon 23 c++ templates sfinae c++11

首选是这样的:

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;
}
Run Code Online (Sandbox Code Playgroud)

或这个:?

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; }
Run Code Online (Sandbox Code Playgroud)

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

我相信它完全一样.

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

And*_*owl 19

我相信它完全一样.

我不会说它完全一样.

在第一个版本中,您使用的是在运行时计算的条件语句,但是决定在哪个分支执行的条件可以在编译时决定.

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

在第二种情况下,您只编译(并执行)适合输入类型的内容.在我看来,这使第二种方法更优越.

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

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

struct X
{
    X(int) { }
};

struct Y
{
    Y() { }
};
Run Code Online (Sandbox Code Playgroud)

以下功能模板:

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

现在以下调用都不合法:

foo(X()); // ERROR! X is not default-constructible
foo(Y()); // ERROR! Y is not constructible from an int
Run Code Online (Sandbox Code Playgroud)

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

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

如果像static ifC++中存在类似的东西,情况当然会有所不同,但目前情况并非如此 - 而且即使在不久的将来也不会如此.