C++ 模板第二版- 完整指南在第 435 页包含以下代码
#include <string>
#include <type_traits>
template<typename T, typename = void>
struct HasBeginT : std::false_type {};
template<typename T>
struct HasBeginT<T, std::void_t<decltype(std::declval<T>().begin())>>
: std::true_type {};
Run Code Online (Sandbox Code Playgroud)
和用于测试调用 adecltype(std::declval<T>().begin())是否有效的注释。.begin()T
这一切都有道理,我想……
令我震惊的是脚注中的评论:
除此之外,与其他上下文中的调用表达式不同,它不需要非引用、非返回类型来完成。使用相反,确实增加了调用返回类型完整的要求,因为返回值不再是操作数的结果。
decltype(call-expression)voiddecltype(std::declval<T>().begin(), 0)decltype
我不太明白。
为了尝试使用它,我尝试使用以下代码查看它对voidmember的行为begin
struct A {
void begin() const;
};
struct B {
};
static_assert(HasBeginT<A>::value, "");
static_assert(!HasBeginT<B>::value, "");
Run Code Online (Sandbox Code Playgroud)
但无论有没有 . ,这两个断言都通过了, 0。
我最近看到一些基于SFINAE的代码如下所示:
template <typename T>
auto test(T &myclass) -> decltype(myclass.f(), void())
{
// do something here, don't return anything (void)
}
Run Code Online (Sandbox Code Playgroud)
基本上上面的函数使用SFINAE来拒绝所有T没有f()作为成员函数的类型的参数.SFINAE发生在decltype我们有两个用逗号运算符分隔的表达式的地方.如果无法评估第一个表达式,SFINAE会启动并拒绝过载.如果可以计算表达式,那么,由于逗号运算符,void正在从函数返回.
据我明白,void()"构建体"一个void处于未评估上下文对象(是的,这是合法的),然后由所拾取decltype所以void是该函数的返回类型.
我的问题是:为什么我们不能使用void{}呢?它是否具有void在未评估的上下文中"构造" 对象的相同效果?我的编译器(g ++/clang ++)不接受void{}代码
error: compound literal of non-object type 'void'(克++ 4.9 /克++ 5)
和
error: illegal initializer type 'void'(clang ++ 3.5)
这个答案有一个这样的代码片段:
template<class T, class F>
auto f(std::vector<T> v, F fun)
-> decltype( bool( fun(v[0] ) ), void() )
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
它真正编译和工作(至少在Ideone上).
那么,在这种情况下如何推断出类型?
c ++ 11标准真的允许下一行吗?
decltype( bool( fun(v[0] ) ), void() )
Run Code Online (Sandbox Code Playgroud)
我快速浏览了一下,看起来并不合适.在这种情况下,意识错误吗?
c ++ 11标准中的所有示例都是这样的,它们在decltype中只有一种类型:
struct A {
char g();
template<class T> auto f(T t) -> decltype(t + g())
{ return t + g(); }
};
Run Code Online (Sandbox Code Playgroud)
另一个例子 :
void f3() {
float x, &r = x;
[=] {
decltype(x) y1;
decltype((x)) y2 = y1; …Run Code Online (Sandbox Code Playgroud) 在任意类型上执行SFINAE时,通常需要将表达式的结果转换为void.我已经看到了两种方法; 演员无效:
(void)(expr) // or static_cast<void>(expr)
Run Code Online (Sandbox Code Playgroud)
或者,使用带有void prvalue RHS的逗号运算符:
(expr), void()
Run Code Online (Sandbox Code Playgroud)
我的理解是,在两种情况下都要expr评估(对于良好的形式,在非评估的上下文中)和结果(或结果类型,在非评估的上下文中)被丢弃; 在任何一种情况下,即使是病理类也不可能T覆盖T::operator void()或operator,(T, void).(参见:为什么是"运营商无效"不投语法调用?,什么的'无效()'中的'自动F(PARAMS) - > decltype(...,无效())'呢?).
也就是说,这两个成语是等价的,还是在某种情况下,一个人应该优先于另一个(可能是非标准的编译器)?如果没有,是否有任何理由(例如可理解性)偏好一个而不是另一个?
我有点困惑为什么decltype逗号运算符在某些情况下返回引用类型.
例如,在此代码中:
int i = 101;
decltype(1, i) var = i;
var = 20;
printf("%d\n", i); // will print 20
Run Code Online (Sandbox Code Playgroud)
这里,var是int而不是int,但如果我用第二行替换:
decltype(i) var = i;
Run Code Online (Sandbox Code Playgroud)
它将返回int!
任何人都可以解释一下吗?
考虑如何使用decltype()尾随返回类型中的逗号表达式来检查是否可以应用函数:
template <class A>
auto f(A a) -> decltype(check_if_possible(a), return_type(a))
Run Code Online (Sandbox Code Playgroud)
如何否定逗号前的部分以排除check_if_possible(a)已定义的情况?
上下文:
f()是不同的重载函数A。我想解决两个实现之间的歧义重载。其中一个使用check_if_possible(a),另一个在不能应用的情况下工作a。
编辑:std::enable_if也欢迎使用和相关的东西,但我想避免额外的辅助功能/模板,因为有几个功能类似于f()具有不同启用条件的功能。如果这是不可能的,那么这也是一个答案;)