C++ 是否可以断言lambda不是泛型的?

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..

我实现了一个Visit函数(在变量上),它检查变量中当前活动的类型是否与函数签名(更准确地说是第一个参数)匹配。基于这个很好。 比如说

#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