为 sfinae 使用别名模板:语言允许吗?
Using alias templates for sfinae: does the language allow it?
我刚刚发现了以下技巧。它看起来非常接近提议的概念语法之一,在 Clang、GCC 和 MSVC 上完美运行。
template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;
template <typename T>
void foo(require_rvalue<T> val);
我试图通过 "sfinae in type alias" 之类的搜索请求找到它,但一无所获。这种技术有名称吗?语言是否真的允许它?
完整示例:
#include <type_traits>
template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;
template <typename T>
void foo(require_rvalue<T>)
{
}
int main()
{
int i = 0;
const int ic = 0;
foo(i); // fail to compile, as desired
foo(ic); // fail to compile, as desired
foo(std::move(i)); // ok
foo(123); // ok
}
[...] does the language actually allows it?
不能说什么名字,但在我看来这是肯定的。
相关的写法是[temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.
和 sfinae 规则,[temp.deduct]/8:
Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.
采用 require_rvalue<T>
类型的参数确实表现得好像我们替换了那个别名,这给了我们 T&&
或替换失败 - 并且替换失败可以说是在直接上下文中† 替换,"sfinae-friendly" 与硬错误相反。请注意,即使未使用默认类型参数,由于 CWG 1558 (the void_t
rule), we got the addition of [temp.alias]/3:
However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id.
这确保我们仍然替换为默认类型参数以触发所需的替换失败。
问题的第二个未说部分是这是否真的可以作为转发参考。 [temp.deduct.call]/3:
中的规则
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
具有一个模板参数的别名模板是否被视为转发引用?嗯,[temp.alias]/2 说 require_rvalue<T>
等价于 T&&
,T&&
是对的。所以可以说......是的。
所有的编译器都这样对待它,这当然是一个很好的验证。
†虽然,请注意 CWG 1844 的存在和直接上下文的实际定义的缺乏,以及那里的示例也依赖于默认参数的替换失败 - 这是问题国家有实施分歧。
它有效并被允许,因为它依赖标准允许的广泛使用的 C++ 功能:
函数参数中的SFINAE ([temp.over]/1, [temp.deduct]/6, [temp.deduct]/8):
template <typename T>
void foo(T&& v, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr)
{ /* ... */ }
我们不能像 void foo(typename std::enable_if<std::is_rvalue_reference<T&&>::value, T>::type&&)
(CWG#549) 那样推导出实际参数,但是可以使用模板别名来解决这个限制(它是我在问题中提出的技巧)
模板参数声明中的SFINAE ([temp.deduct]/7):
template <typename T, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr>
void foo(T&& v)
{ /* ... */ }
函数参数中的别名模板([temp.alias]/2):
template<class T> struct Alloc { /* ... */ };
template<class T> using Vec = vector<T, Alloc<T>>;
template<class T>
void process(Vec<T>& v)
{ /* ... */ }
别名模板可以有默认参数([temp.param]/12, [temp.param]/15, [temp.param]/18)
使用可推导类型参数化的别名模板的模板参数仍然可以推导([temp.deduct.type]/17):
我接受了@Barry 的回答并提出了这个(包含集中信息和技巧使用的各个方面)因为很多人(包括我)害怕关于模板的 C++ 标准巫术语言扣东西
我刚刚发现了以下技巧。它看起来非常接近提议的概念语法之一,在 Clang、GCC 和 MSVC 上完美运行。
template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;
template <typename T>
void foo(require_rvalue<T> val);
我试图通过 "sfinae in type alias" 之类的搜索请求找到它,但一无所获。这种技术有名称吗?语言是否真的允许它?
完整示例:
#include <type_traits>
template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;
template <typename T>
void foo(require_rvalue<T>)
{
}
int main()
{
int i = 0;
const int ic = 0;
foo(i); // fail to compile, as desired
foo(ic); // fail to compile, as desired
foo(std::move(i)); // ok
foo(123); // ok
}
[...] does the language actually allows it?
不能说什么名字,但在我看来这是肯定的。
相关的写法是[temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.
和 sfinae 规则,[temp.deduct]/8:
Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.
采用 require_rvalue<T>
类型的参数确实表现得好像我们替换了那个别名,这给了我们 T&&
或替换失败 - 并且替换失败可以说是在直接上下文中† 替换,"sfinae-friendly" 与硬错误相反。请注意,即使未使用默认类型参数,由于 CWG 1558 (the void_t
rule), we got the addition of [temp.alias]/3:
However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id.
这确保我们仍然替换为默认类型参数以触发所需的替换失败。
问题的第二个未说部分是这是否真的可以作为转发参考。 [temp.deduct.call]/3:
中的规则A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
具有一个模板参数的别名模板是否被视为转发引用?嗯,[temp.alias]/2 说 require_rvalue<T>
等价于 T&&
,T&&
是对的。所以可以说......是的。
所有的编译器都这样对待它,这当然是一个很好的验证。
†虽然,请注意 CWG 1844 的存在和直接上下文的实际定义的缺乏,以及那里的示例也依赖于默认参数的替换失败 - 这是问题国家有实施分歧。
它有效并被允许,因为它依赖标准允许的广泛使用的 C++ 功能:
函数参数中的SFINAE ([temp.over]/1, [temp.deduct]/6, [temp.deduct]/8):
template <typename T> void foo(T&& v, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr) { /* ... */ }
我们不能像
void foo(typename std::enable_if<std::is_rvalue_reference<T&&>::value, T>::type&&)
(CWG#549) 那样推导出实际参数,但是可以使用模板别名来解决这个限制(它是我在问题中提出的技巧)模板参数声明中的SFINAE ([temp.deduct]/7):
template <typename T, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr> void foo(T&& v) { /* ... */ }
函数参数中的别名模板([temp.alias]/2):
template<class T> struct Alloc { /* ... */ }; template<class T> using Vec = vector<T, Alloc<T>>; template<class T> void process(Vec<T>& v) { /* ... */ }
别名模板可以有默认参数([temp.param]/12, [temp.param]/15, [temp.param]/18)
使用可推导类型参数化的别名模板的模板参数仍然可以推导([temp.deduct.type]/17):
我接受了@Barry 的回答并提出了这个(包含集中信息和技巧使用的各个方面)因为很多人(包括我)害怕关于模板的 C++ 标准巫术语言扣东西