我使用的是C++17。我制作了以下代码,应该使用SFINAE来测试lambda是否可编译(lambda在语法上总是正确的,但可能是不可编译的,例如,由于在body中没有使用一些方法(:
在线试用!
#include <type_traits>
#include <sstream>
#include <iostream>
#include <tuple>
template <typename ... Ts>
struct Types {
using types = std::tuple<Ts...>;
template <size_t I>
using type = std::tuple_element_t<I, types>;
};
template <typename F, typename Enable = void, typename ... Ts>
struct Compilable : std::false_type {};
template <typename F, typename ... Ts>
struct Compilable<F,
std::void_t<decltype((*(F*)0)(Types<Ts...>{}))>, Ts...>
: std::true_type {};
template <typename ... Ts, typename F>
bool constexpr Comp(F const & f) {
return Compilable<F, void, Ts...>::value;
}
template <typename T>
void Test() {
std::cout << Comp<T>([](auto Ts){
typename decltype(Ts)::template type<0> * p = 0;
std::stringstream ss; ss << (*p); }) << std::endl;
}
struct X {};
int main() {
Test<int>();
Test<X>();
}
我希望由于SFINAE,不可编译的专业化将被默默地排除在外,但它却抛出编译错误(对于第二个Test<X>()
案例,第一个测试编译(:
<source>:30:34: error: invalid operands to binary expression
('std::stringstream' (aka 'basic_stringstream<char>') and
'typename decltype(Ts)::type<0>' (aka 'X'))
std::stringstream ss; ss << (*p); }) << std::endl;
我做错了什么?我是否错误地使用了SFINAE机制?实现上述代码的正确方法应该是什么?
我认为问题是在lambda的主体中尝试基于SFINAE的错误。如果我理解这一点,那么从错误中恢复过来就太晚了。
SFINAE的基本思想是从重载集中删除不兼容的函数,而不是从编译失败中恢复,行有点模糊,但最好的经验法则可能是,错误只需要在函数声明中发生。
例如
template<typename T>
struct Get
{
using type = typename T::type;
};
void F1(...){}
template<typename T, typename TT = typename Get<T>::type>
void F1(T i){}
void F2(...){}
template<typename T, typename TT = typename T::type>
void F2(T i){}
int main() {
//F1(3); //hard error
F2(3); //SFINAE - work fine
}
类型Get<T>
无法创建,理论上编译器可以从中恢复,但这可能代价高昂(图像错误发生在深层结构中(。F2
正确地失败了,因为编译器只需要查看函数头就可以确定它是否正确。
如果您可以将所需的检查移动到模板标题,那么理论上它可以工作。类似于:
template <typename T>
void Test() {
std::cout << Comp<T>(
[](auto Ts, decltype((*(std::stringstream*)0) << (*(typename decltype(Ts)::template type<0>*)0))* = {})
{
typename decltype(Ts)::template type<0> * p = 0;
std::stringstream ss; ss << (*p);
}
) << std::endl;
}
第二lambda参数在调用站点上进行评估;明显地";应用参数失败。它并没有给出您想要的结果,但它是编译的,您可能需要更新pram类型来正确反映lambda中的操作。