是否有可能 static_assert lambda 不是通用的?
Is it possible to static_assert that a lambda is not generic?
我实现了一个 Visit 函数(在变体上),它检查变体中当前活动的类型是否与函数签名(更准确地说是第一个参数)匹配。基于这个nice answer。
例如
#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
}
这行得通,但当用户传递通用(例如 [](auto&){}
)lambda 时,它会引发用户不友好的编译时错误。有没有办法检测到这一点并给予好评 static_assert()
?
如果它也能与函数模板一起工作,而不仅仅是与 lambdas 一起工作,那就太好了。
请注意,我不知道 lambda 可以做什么,所以我不能用 Dummy 类型做一些聪明的事情,因为 lambda 可能会调用类型上的任意函数。
换句话说,我不能尝试在 int
和 std::string
的 2 std::void_t
测试中调用 lambda,如果它有效,则假设它是通用的,因为他们可能会尝试调用 .BlaLol()
int
和 string
.
Is there a way to detect this and give nice static_assert about it?
我想你可以使用 SFINAE 而不是 operator()
类型。
举个例子
#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), "!" );
}
而不是 bool
,如果需要,您可以 return std::true_type
(来自 foo()
第一版)或 std::false_type
(来自第二版)通过 decltype()
.
使用它
Would also be nice if it worked with function templates as well, not just with lambdas.
我认为不可能以如此简单的方式实现:lambda(也是通用 lambda)是一个对象;模板函数不是一个对象而是一组对象。您可以将一个对象传递给一个函数,而不是一组对象。
但前面的解决方案也适用于 classes/structs 和 operator()
s:当有一个非模板 operator()
时,您应该从 1
foo()
;否则(没有operator()
,不止一个operator()
,模板operator()
),foo()
应该return0
.
#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
}
我不知道你是否想要,但是你可以,static_assert 如果自动 lambda 作为参数传递。
我认为不可能对模板函数做同样的事情,但不确定。
还有一个更简单的选择:
#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);
...
}
Auto
class 只是一个发明的类型,不可能出现在 V
参数中。如果 V
接受 Auto
作为参数,它必须是泛型。
我在 coliru 中进行了测试,我可以确认解决方案涵盖了这些情况:
Visit([](auto x){}); // nice static assert
Visit([](auto *x){}); // nice static assert
Visit([](auto &x){}); // nice static assert
Visit([](auto &&x){}); // nice static assert
我不确定这是否涵盖了您不知道的所有可能的 lambda 表达式:)
我实现了一个 Visit 函数(在变体上),它检查变体中当前活动的类型是否与函数签名(更准确地说是第一个参数)匹配。基于这个nice answer。 例如
#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
}
这行得通,但当用户传递通用(例如 [](auto&){}
)lambda 时,它会引发用户不友好的编译时错误。有没有办法检测到这一点并给予好评 static_assert()
?
如果它也能与函数模板一起工作,而不仅仅是与 lambdas 一起工作,那就太好了。
请注意,我不知道 lambda 可以做什么,所以我不能用 Dummy 类型做一些聪明的事情,因为 lambda 可能会调用类型上的任意函数。
换句话说,我不能尝试在 int
和 std::string
的 2 std::void_t
测试中调用 lambda,如果它有效,则假设它是通用的,因为他们可能会尝试调用 .BlaLol()
int
和 string
.
Is there a way to detect this and give nice static_assert about it?
我想你可以使用 SFINAE 而不是 operator()
类型。
举个例子
#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), "!" );
}
而不是 bool
,如果需要,您可以 return std::true_type
(来自 foo()
第一版)或 std::false_type
(来自第二版)通过 decltype()
.
Would also be nice if it worked with function templates as well, not just with lambdas.
我认为不可能以如此简单的方式实现:lambda(也是通用 lambda)是一个对象;模板函数不是一个对象而是一组对象。您可以将一个对象传递给一个函数,而不是一组对象。
但前面的解决方案也适用于 classes/structs 和 operator()
s:当有一个非模板 operator()
时,您应该从 1
foo()
;否则(没有operator()
,不止一个operator()
,模板operator()
),foo()
应该return0
.
#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
}
我不知道你是否想要,但是你可以,static_assert 如果自动 lambda 作为参数传递。
我认为不可能对模板函数做同样的事情,但不确定。
还有一个更简单的选择:
#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);
...
}
Auto
class 只是一个发明的类型,不可能出现在 V
参数中。如果 V
接受 Auto
作为参数,它必须是泛型。
我在 coliru 中进行了测试,我可以确认解决方案涵盖了这些情况:
Visit([](auto x){}); // nice static assert
Visit([](auto *x){}); // nice static assert
Visit([](auto &x){}); // nice static assert
Visit([](auto &&x){}); // nice static assert
我不确定这是否涵盖了您不知道的所有可能的 lambda 表达式:)