以下内容无法编译:
#include <iostream>
#include <type_traits>
template <class F, class G>
auto static_if (std::true_type, F && f, G && g) {
return std::forward<F> (f) (0);
}
template <class F, class G>
auto static_if (std::false_type, F && f, G && g) {
return std::forward<G> (g) (0);
}
template <class T> std::true_type constexpr is_pointer (T *) { return {}; }
template <class T> std::false_type constexpr is_pointer (T) { return {}; }
int main() {
int x = 5;
static_if (
is_pointer (x),
[&] (auto) { std::cout << *x << std::endl; },
[&] (auto) { std::cout << "no pointer" << std::endl; }
);
return 0;
}
编译失败,并显示错误消息:
prog.cpp: In lambda function:
prog.cpp:23:33: error: invalid type argument of unary '*' (have 'int')
[&] (auto) { std::cout << *x << std::endl; },
现在问题是:为什么?
很明显,如果lambdas不是通用的(没有auto
参数),编译应该会失败。
但是我们这里有两个泛型 lambdas。它们中的每一个都只应在使用时进行实例化(这意味着:称为,或者至少在以某种方式引用operator ()
时)。
因此,生成错误消息的通用lambda应不实例化,因为使用了static_if
的第二个重载,在参数f
<的定义中em> not 被叫。由于未使用通用lambda operator ()
,编译器应该简单地忽略它。
我的推理在哪里错了?
答案 0 :(得分:4)
在对已接受答案的评论中,您会问是否有更好的解决方案。可以说下面是一个(实时代码here)。你不是在本地捕获x,而是将static_if
概括为一般地将任何参数转发给两个lambdas中的任何一个,并使你的x
成为这样的参数。那么你不需要在评论中使用虚拟变量跳舞。
template <class F, class G, class ... Args>
auto static_if (std::true_type, F && f, G && g, Args && ... args) {
return std::forward<F> (f) (std::forward<Args>(args)...);
}
template <class F, class G, class ... Args>
auto static_if (std::false_type, F && f, G && g, Args && ... args) {
return std::forward<G> (g) (std::forward<Args>(args)...);
}
template <class T> std::true_type constexpr is_pointer (T *) { return {}; }
template <class T> std::false_type constexpr is_pointer (T) { return {}; }
int main() {
int x = 5;
static_if (
is_pointer (x),
[] (auto x) { std::cout << *x << std::endl; },
[] (auto x) { std::cout << "no pointer" << std::endl; },
x
);
return 0;
}
答案 1 :(得分:3)
x
是非模板依赖类型,因此检查在模板检查的第一次传递中完成,因为即使对于未实例化的模板,static_assert(false, "")
也会触发。
答案 2 :(得分:1)
[&x](auto){ std::cout<<*x<<'\n'; }
是一个带有模板operator()
的lambda。没有传递给它们的可能类型的模板operator()
在C ++中是不合法的。
*x
的有效性不会因模板参数而改变。
解决此问题的一个好办法是改变static_if
的工作方式。
template <class F, class G>
auto static_branch (std::true_type, F && f, G && g) {
return std::forward<F>(f);
}
template <class F, class G>
auto static_branch (std::false_type, F && f, G && g) {
return std::forward<G>(g);
}
template <bool test, class F, class G>
auto static_branch(F && f, G && g) {
return static_branch( std::integral_constant<bool, test>{}, std::forward<F>(f), std::forward<G>(g) );
}
请注意,我们不会调用f
或g
。相反,我们会返回一个或另一个。
int main() {
int x = 5;
auto branch = static_branch (
is_pointer (x),
[&] (auto&&x) { std::cout << *x << std::endl; },
[&] (auto&&) { std::cout << "no pointer" << std::endl; }
);
branch(x);
return 0;
}
现在x
传递到相应的lambda。这意味着只有当x
是指针时才会评估*x
代码。
int* px = &x;
static_branch (
is_pointer(y),
[&] (auto&&x) { std::cout << *x << std::endl; },
[&] (auto&&) { std::cout << "no pointer" << std::endl; }
)(y);
然后将调用第一个分支。
您还可以等待C ++ 17,其中constexpr if
可以解决您的问题;我不确定。