无用知识研究:在trailing return type利用decltype,comma operator在对函数进行sfinae原创 [三]
感觉利用返回类型后置的写法,比在函数的参数列表里加参数,要方便很多。
template<typname T, std::enable_if_t<std::is_floating_v<T>,int>*=0>
void fun(T&& t)
{
}
只有一个函数的版本,只匹配floating:
注意:
std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>, int>(),的写法,
它不仅促使编译器进行substitution,也deduce出了一个类型:int。所以让函数模板
的“重载”带有sfinae的功能。1、如果写成std::is_floating_point_v<std::remove_reference_t<T>>,可能可以编译成功,但匹配时可能有substitution,但没有deduce,所以可以编译成功,但匹配的可能不对。
2、如果把()写成{},也就是写成std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>, int>{},
也会出现:编译成功,但其实匹配不对的问题。#include <iostream>
#include <string>
#include <type_traits>
#include <iostream>
#include <functional>/////////////////////////
// 过滤是否为floating类型
/////////////////////////
template<typename T>
auto check_double_and_return(T&& t) -> decltype(std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>, int>(),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is floating" << std::endl;return std::forward<T>(t);
}int main()
{int x = 42;const std::string& str = "test";///////////////////////////// 检查double///////////////////////////double f = 1.0;//static_assert(!std::is_floating_point_v<std::remove_reference_t<double>>, "Type cannot be double!"),//std::cout << check_double_and_return(f) << std::endl;std::cout << check_double_and_return(x) << std::endl; //这行编译错误,符合预期,而且也没有默认的函数满足sfinae,所以只能编译报错}编译错误信息:
error C2672: “check_double_and_return”: 未找到匹配的重载函数
error C2893: 未能使函数模板“unknown-type check_double_and_return(T &&)”专用化
note: 用下列模板参数:
note: “T=int &”
除了floating,还增加一个检验class的版本:
#include <iostream>
#include <string>
#include <type_traits>
#include <iostream>
#include <functional>/////////////////////
// 匹配浮点数的变量
/////////////////////
template<typename T>
auto check_double_and_return(T&& t) -> decltype(// 逗号表达式的第一个部分:一个编译期断言///*static_assert*/(!std::is_floating_point_v<std::remove_reference_t<T>>, "Type cannot be floating!"),std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>, int>(),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is floating" << std::endl;return std::forward<T>(t);
}/////////////////////
// 匹配class类型的变量
/////////////////////
template<typename T>
auto check_double_and_return(T&& t) -> decltype(// 逗号表达式的第一个部分:一个编译期断言///*static_assert*/(!std::is_floating_point_v<std::remove_reference_t<T>>, "Type cannot be floating!"),std::enable_if_t<std::is_class_v<std::remove_reference_t<T>>, int>(),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is class" << std::endl;return std::forward<T>(t);
}class CTest
{};int main()
{int x = 42;const std::string& str = "test";///////////////////////////// 检查double///////////////////////////double f = 1.0;//static_assert(!std::is_floating_point_v<std::remove_reference_t<double>>, "Type cannot be double!"),//auto v = check_double_and_return(f); //匹配上第一个std::cout << v << std::endl;//std::cout << check_double_and_return(x) << std::endl;CTest o;check_double_and_return(o); //匹配上第二个
}运行结果:
is floating
1
is class
来一个sfinae版的,当既不匹配floating,又不匹配class的情况:
#include <iostream>
#include <string>
#include <type_traits>
#include <iostream>
#include <functional>template<typename T>
auto check_double_and_return(T&& t) -> decltype(// 逗号表达式的第一个部分:一个编译期断言///*static_assert*/(!std::is_floating_point_v<std::remove_reference_t<T>>, "Type cannot be floating!"),std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>, int>(),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is floating" << std::endl;return std::forward<T>(t);
}template<typename T>
auto check_double_and_return(T&& t) -> decltype(// 逗号表达式的第一个部分:一个编译期断言///*static_assert*/(!std::is_floating_point_v<std::remove_reference_t<T>>, "Type cannot be floating!"),std::enable_if_t<std::is_class_v<std::remove_reference_t<T>>, int>(),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is class" << std::endl;return std::forward<T>(t);
}auto check_double_and_return(...) -> void
{std::cout << "is default" << std::endl;}// template<typename T>
// auto check_double_and_return(...) -> void
// {
// std::cout << "is default" << std::endl;
// }class CTest
{};enum MyEnum
{hhh
};
int main()
{int x = 42;const std::string& str = "test";// 正常工作//std::cout << check_and_return(x) << std::endl;//std::cout << check_and_return(str) << std::endl;// 下面这行会导致编译错误,因为 static_assert 失败//check_and_return(static_cast<void>(x)); //std::cout << check_string_and_return(str) << std::endl;///////////////////////////// 检查double///////////////////////////double f = 1.0;//static_assert(!std::is_floating_point_v<std::remove_reference_t<double>>, "Type cannot be double!"),//auto v = check_double_and_return(f);std::cout << v << std::endl;//std::cout << check_double_and_return(x) << std::endl;CTest o;check_double_and_return(o); //匹配上classcheck_double_and_return(str); //也匹配上classMyEnum e = MyEnum::hhh;check_double_and_return(e); //匹配上默认的
}运行结果:
is floating
1
is class
is class
is default
注意,这些限制里,不能有交集,否则会报error C2668: “check_double_and_return”: 对重载函数的调用不明确。什么叫交集呢,比如一个check_double_and_return里规定需要是一个class,另一个check_double_and_return规定需要是一个class,并且这个class要有emplace_back函数。这种情况下,就会编译报错。
#include <iostream>
#include <string>
#include <type_traits>
#include <iostream>
#include <functional>
#include <vector>template<typename T>
auto check_double_and_return(T&& t) -> decltype(// 逗号表达式的第一个部分:一个编译期断言///*static_assert*/(!std::is_floating_point_v<std::remove_reference_t<T>>, "Type cannot be floating!"),std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>, int>(),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is floating" << std::endl;return std::forward<T>(t);
}template<typename T>
auto check_double_and_return(T&& t) -> decltype(// 逗号表达式的第一个部分:一个编译期断言///*static_assert*/(!std::is_floating_point_v<std::remove_reference_t<T>>, "Type cannot be floating!"),std::enable_if_t<std::is_class_v<std::remove_reference_t<T>>, int>(),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is class" << std::endl;return std::forward<T>(t);
}template<typename T>
auto check_double_and_return(T&& t) -> decltype(// 逗号表达式的第一个部分:一个编译期断言///*static_assert*/(!std::is_floating_point_v<std::remove_reference_t<T>>, "Type cannot be floating!"),std::enable_if_t<std::is_class_v<std::remove_reference_t<T>>, int>(),t.emplace_back(0),// 逗号表达式的第二个(最后一个)部分:决定函数返回类型的表达式std::forward<T>(t))
{std::cout << "is container" << std::endl;return std::forward<T>(t);
}auto check_double_and_return(...) -> void
{std::cout << "is default" << std::endl;}class CTest
{};enum MyEnum
{hhh
};int main()
{int x = 42;const std::string& str = "test";///////////////////////////// 检查double///////////////////////////double f = 1.0;//static_assert(!std::is_floating_point_v<std::remove_reference_t<double>>, "Type cannot be double!"),//auto t = check_double_and_return(f);std::cout << t << std::endl;//std::cout << check_double_and_return(x) << std::endl;CTest o;check_double_and_return(o); //匹配上classcheck_double_and_return(str); //也匹配上classMyEnum e = MyEnum::hhh;check_double_and_return(e); //匹配上默认的std::vector<int> v;check_double_and_return(v); //编译错误,因为第二个和第三个都符合要求。所以选不出来,删除其中一个就可以编译成功。
}
编译提示如下: