模板中 std::enable_if<> 的不同用法

我正在尝试了解使用 std::enable_if<> 的模板函数的不同版本。

版本 1:

template<class T,typename std::enable_if<std::is_convertible<T,std::string_view>::value,T>::type* = nullptr>
void foo(const T& msg);

版本 2:

template<class T,typename = typename std::enable_if<std::is_convertible<T,std::string_view>::value>::type>
void foo(const T& msg);

如果我理解正确,如果满足条件,它们应该转换为:

// Version 1
template<class T,T* = nullptr>
void foo(const T& msg);

// Version 2
template<class T,typename = void>
void foo(const T& msg);

两个版本都可以通过以下方式同等调用:

std::string s = "Test";
foo(s);

这两个版本有什么区别?什么时候用?


第二个问题

由于我的一个错误,我发现如果缺少一个类型名,版本 2 也可以编译:

//Correct Version 2 like above:
template<class T,std::string_view>::value>::type>
void foo(const T& msg);

// My error version. Also works. Is this also correct?
template<class T,typename = std::enable_if<std::is_convertible<T,std::string_view>::value>::type>
void foo(const T& msg);

第二个版本也正确吗?我认为 std::enable_if<> 前面确实需要一个 typename

jasonzhyh 回答:模板中 std::enable_if<> 的不同用法

应该如何约束模板?

如果您不限于与旧的 C++ 标准的兼容性,并且您不需要引用模板类型,并且约束仅涉及单个模板参数,则首选最少样板选项:

// #1
void foo(const std::convertible_to<std::string_view> auto& msg);

否则,更喜欢稍微冗长的形式:

// #2
template <typename T>
    requires std::convertible_to<T,std::string_view>
void foo(const T& msg);

表单#2 为模板类型命名,如果约束涉及多个模板参数,则它继续起作用。它仍然不能直接适用于较旧的 C++,但约束的位置与较旧的 C++ enable_if 用法兼容:

// #2,compatible version

// C++11
#define TEMPLATE(...)            template <__VA_ARGS__
#define REQUIRES(C),typename std::enable_if<(C),int>::type = 0>
#define CONVERTIBLE_TO(From,To) std::is_convertible<From,To>::value

// C++20
#define TEMPLATE(...)            template <__VA_ARGS__>
#define REQUIRES(C)              requires (C)
#define CONVERTIBLE_TO(From,To) std::convertible_to<From,To>

TEMPLATE(typename T)
    REQUIRES(CONVERTIBLE_TO(T,std::string_view))
void foo(const T& msg);

以下选项也可用,但我会坚持 #1 或 #2:

// #3
template <std::convertible_to<std::string_view> T>
void foo(const T& msg);

// #4
template <typename T>
void foo(const T& msg) requires std::convertible_to<T,std::string_view>;

关于enable_if,有三个选项:

// #5,non-type template parameter with default value ("version 1")
template <typename T,typename std::enable_if_t<std::is_convertible_v<T,std::string_view>,int> = 0>
void foo(const T& msg);

// #6,enable_if in the return type
template<typename T>
auto foo(const T& msg) -> typename std::enable_if_t<std::is_convertible_v<T,std::string_view>>;

// #7,defaulted template parameter ("version 2")
template<class T,typename = typename std::enable_if_t<std::is_convertible_v<T,std::string_view>>>
void foo(const T& msg);

选项 #7(“版本 2”)很少可取,因为默认模板参数不参与函数签名。所以,一旦你有两个重载,它就是模棱两可的。并且过载集会增长。

选项 #6 不适用于缺少返回类型的构造函数。但是,在 #6 中,您可以命名函数参数,这很方便。

选项#5 是最通用的 SFINAE 选项。喜欢它,如果你必须 SFINAE。

关于问题#2,C++20 中对 typename 进行了放宽,描述为 herehere

本文链接:https://www.f2er.com/158.html

大家都在问