为SFINAE测试人员提供默认值零的原因是什么?

问题描述 投票:0回答:2

我注意到很多 boost 和 libc++/libstdc++ 在代码中明确为 SFINAE 提供默认值零,例如

// libc++ http://llvm.org/svn/llvm-project/libcxx/trunk/include/memory
namespace __has_pointer_type_imp
{
    template <class _Up> static __two __test(...);
    template <class _Up> static char __test(typename _Up::pointer* = 0);
}

template <class _Tp>
struct __has_pointer_type
    : public integral_constant<bool, sizeof(__has_pointer_type_imp::__test<_Tp>(0)) == 1>
{
};

然而,当他们明确地用 0 进行调用时,为什么会出现这种情况让我感到困惑。我记得在某处听到它是一种优化(在实例化模板时加速编译器),但我不完全理解那是怎么回事工作。我查看了标准,它有一个部分简要描述了默认参数与模板参数推导相关的情况。

14.8.2

在模板参数推导过程中的某些点,有必要采用使用模板参数的函数类型,并将这些模板参数替换为相应的模板参数。当任何明确指定的模板参数被替换为函数类型时,这是在模板参数推导开始时完成的,并且当任何从默认参数推导或获得的模板参数被替换时,在模板参数推导结束时再次完成。

最后一点听起来与我的问题有关

当从默认参数推导或获得的任何模板参数被替换时,在模板参数推导结束时再次出现。

然而,如果它必须做更多的工作,这听起来像是优化的反面。有没有人知道为什么 0 必须在那里,没有它它也能工作,但是 libc++ 中的每个 SFINAE 示例至少似乎明确地将 0 放在那里,即使他们从不调用没有参数的函数。

c++ templates c++11 sfinae libc++
2个回答
0
投票

在 SFINAE 代码中使用 0 作为默认参数是一个已经存在了很长时间的约定,但这并不是代码正确工作所必需的。事实上,许多现代 C++ 库和框架(例如 MSVC 中的标准库实现)为此目的使用 nullptr 而不是 0。

在 SFINAE 代码中使用 0 或 nullptr 的原因与优化无关,而是为了避免在被测试的函数有自己的默认参数的情况下产生歧义。例如,考虑以下代码:

template<typename T>
auto test(int, typename T::type* = nullptr) -> std::true_type;

template<typename T>
auto test(int, ...) -> std::false_type;

struct A {
    using type = int;
};

int main() {
    static_assert(test<A>(0), "");
    static_assert(!test<int>(0), "");
}

这里,test 的第一个重载使用 SFINAE 检查 T::type 是否存在,如果存在则返回 std::true_type。第二个重载是一个回退,它为任何没有嵌套类型成员的类型返回 std::false_type。

如果第一个重载没有默认参数 nullptr,则对 test(0) 的调用将不明确,因为第二个重载也会匹配(第二个参数为 0)。通过提供保证与任何其他值不同的默认参数,第一个重载明确地变得更加具体,并获得正确的结果。

总而言之,在 SFINAE 代码中使用 0 或 nullptr 作为默认参数是一种避免歧义的约定,但这并不是代码正确工作所必需的。它与优化无关。


-1
投票

你给了一个默认值,因为你希望能够不给那个参数,尽管条件仍然是检查!

例子:

template<typename T,
        typename std::enable_if<
                std::is_floating_point_v<T>
                               >::type = 0>
void foo() { std::cout << "1"; }

然后可以只用一个模板参数在主函数中调用这个函数!更好的是,甚至不需要命名第二个参数,因为它未被使用。

在函数的输入中给定参数时的类似例子

foo

template<class T>
void foo(T t,
         typename std::enable_if<
                       std::is_floating_point_v<T>
                  >::type = 0) { std::cout << "2"; }

然后你可以给函数第二个参数

foo
但你一般不想这样做。

© www.soinside.com 2019 - 2024. All rights reserved.