C++ 是否可以断言lambda不是泛型的?
我实现了一个Visit函数(在变量上),它检查变量中当前活动的类型是否与函数签名(更准确地说是第一个参数)匹配。基于这个很好。 比如说C++ 是否可以断言lambda不是泛型的?,c++,c++17,variadic-templates,template-meta-programming,generic-lambda,C++,C++17,Variadic Templates,Template Meta Programming,Generic Lambda,我实现了一个Visit函数(在变量上),它检查变量中当前活动的类型是否与函数签名(更准确地说是第一个参数)匹配。基于这个很好。 比如说 #include <variant> #include <string> #include <iostream> template<typename Ret, typename Arg, typename... Rest> Arg first_argument_helper(Ret(*) (Arg, Rest..
#include <variant>
#include <string>
#include <iostream>
template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);
template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);
template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data)) {
std::cerr<< "alternative mismatch\n";
return;
}
v(std::get<Arg1>(data));
}
int main(){
Visit([](const int& i){std::cout << i << "\n"; });
Visit([](const std::string& s){std::cout << s << "\n"; });
// Visit([](auto& x){}); ugly kabooom
}
#包括
#包括
#包括
模板
Arg第一个参数辅助对象(Ret(*)(Arg,Rest…);
模板
Arg第一个参数助手(Ret(F::*)(Arg,Rest…);
模板
Arg第一个参数(Ret(F::*)(Arg,Rest…)常量);
模板
decltype(第一个参数辅助对象(&F::operator()))第一个参数辅助对象(F);
模板
使用first_argument=decltype(first_argument_helper(std::declval());
std::variant data=“abc”;
模板
无效访问(V){
使用Arg1=typename std::remove_const_t;/…TMP magic获得visitor+remove cvr的第一个参数,请参见Q 43526647
如果(!std::持有_备选方案(数据)){
标准:cerr
有没有一种方法可以检测到这一点,并给出一个很好的静态断言
我想您可以在operator()
type上使用SFINAE
举个例子
#include <type_traits>
template <typename T>
constexpr auto foo (T const &)
-> decltype( &T::operator(), bool{} )
{ return true; }
constexpr bool foo (...)
{ return false; }
int main()
{
auto l1 = [](int){ return 0; };
auto l2 = [](auto){ return 0; };
static_assert( foo(l1), "!" );
static_assert( ! foo(l2), "!" );
}
#包括
模板
constexpr auto foo(T const&)
->decltype(&T::operator(),bool{})
{返回true;}
constexpr bool foo(…)
{返回false;}
int main()
{
自动l1=[](int){返回0;};
自动l2=[](自动){返回0;};
静态断言(foo(l1),“!”;
静态断言(!foo(l2),“!”);
}
如果要通过decltype()
使用,可以返回std::true\u type
(从foo()
第一个版本)或std::false\u type
(从第二个版本)而不是bool
如果它也能与函数模板一起工作,而不仅仅是与lambdas一起工作,那就太好了
我认为用这么简单的方式是不可能的:lambda(也是通用lambda)是一个对象;模板函数不是一个对象,而是一组对象。你可以将一个对象传递给函数,而不是一组对象
但是前面的解决方案也应该适用于带有operator()
s的类/结构:当存在单个非模板的operator()
时,您应该从foo()
中获得1
;否则(没有operator()
,多个operator()
,模板operator()
),foo()
应返回0
\include
#include <variant>
#include <string>
#include <iostream>
template <class U, typename T = void>
struct can_be_checked : public std::false_type {};
template <typename U>
struct can_be_checked<U, std::enable_if_t< std::is_function<U>::value > > : public std::true_type{};
template <typename U>
struct can_be_checked<U, std::void_t<decltype(&U::operator())>> : public std::true_type{};
template<typename Ret, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...));
template<typename Ret, typename F, typename Arg, typename... Rest>
Arg first_argument_helper(Ret(F::*) (Arg, Rest...) const);
template <typename F>
decltype(first_argument_helper(&F::operator())) first_argument_helper(F);
template <typename T>
using first_argument = decltype(first_argument_helper(std::declval<T>()));
std::variant<int, std::string> data="abc";
template <typename V>
void Visit(V v){
if constexpr ( can_be_checked<std::remove_pointer_t<decltype(v)>>::value )
{
using Arg1 = typename std::remove_const_t<std::remove_reference_t<first_argument<V>>>;//... TMP magic to get 1st argument of visitor + remove cvr, see Q 43526647
if (! std::holds_alternative<Arg1>(data))
{
std::cerr<< "alternative mismatch\n";
return;
}
v(std::get<Arg1>(data));
}
else
{
std::cout << "it's a template / auto lambda " << std::endl;
}
}
template <class T>
void foo(const T& t)
{
std::cout <<t << " foo \n";
}
void fooi(const int& t)
{
std::cout <<t << " fooi " << std::endl;
}
int main(){
Visit([](const int& i){std::cout << i << std::endl; });
Visit([](const std::string& s){std::cout << s << std::endl; });
Visit([](auto& x){std::cout <<x << std::endl;}); // it's a template / auto lambda*/
Visit(foo<int>);
Visit<decltype(fooi)>(fooi);
Visit(fooi);
// Visit(foo); // => fail ugly
}
#包括
#包括
模板
可以检查结构:public std::false\u type{};
模板
可以检查结构>:public std::true\u type{};
模板
可以检查结构:public std::true\u type{};
模板
Arg第一个参数辅助对象(Ret(*)(Arg,Rest…);
模板
Arg第一个参数助手(Ret(F::*)(Arg,Rest…);
模板
Arg第一个参数(Ret(F::*)(Arg,Rest…)常量);
模板
decltype(第一个参数辅助对象(&F::operator()))第一个参数辅助对象(F);
模板
使用first_argument=decltype(first_argument_helper(std::declval());
std::variant data=“abc”;
模板
无效访问(V){
if constexpr(可检查::值)
{
使用Arg1=typename std::remove_const_t;/…TMP magic获得visitor+remove cvr的第一个参数,请参见Q 43526647
如果(!std::持有_备选方案(数据))
{
std::cerr另一个更简单的选项:
#include <type_traits>
...
template <typename V>
void Visit(V v) {
class Auto {};
static_assert(!std::is_invocable<V, Auto&>::value);
static_assert(!std::is_invocable<V, Auto*>::value);
...
}
我不确定这是否涵盖了所有可能的lambda,您不知道这些lambda是:)如果functor有一个重载的操作符()
,该怎么办?访问也通常使用重载的functor执行(参见示例4),是否必须禁止(或必须工作)?我认为这太难处理了,但如果能做到这一点,那就太好了……因此它是可选的,不是必需的。尝试获取operator()
的地址也是我最初的想法(因此评论)但是后来我在SFINAE中迷了路,实际上调用它。无论如何,这里有一些涉及重载函子的额外测试用例:好主意!您可以确保“Auto”是唯一的,就像这样:Auto a_lambda=[](){};使用Auto=decltype(a_lambda)
@Martinm这似乎是惯用的方式,但在这个答案中,它实际上比代码有优势吗?嗯,我不确定在“自动”的情况下会发生什么类已在其他地方定义。在元编程上下文中,我更喜欢确保我的类型在某些模糊情况下不会冲突。这会产生误报(例如Visit([](std::any){};
)和误报(Visit([](int,auto){});
或Visit([](auto*){};
)Visit([](int,auto){})由于v(std::get(data));
和Visit([](auto*){});
现在已修复,因此不是有效的情况。我不确定Visit([](std::any){})
是否是需要避免的情况之一,因为问题还不够清楚。
Visit([](auto x){}); // nice static assert
Visit([](auto *x){}); // nice static assert
Visit([](auto &x){}); // nice static assert
Visit([](auto &&x){}); // nice static assert