[英]Enforcing const-correctness when converting lambdas to std::function
I have a function that takes in a std::function
as a parameter. 我有一个函数,它接受一个std::function
作为参数。 However, I want to ensure the function being passed in is not allowed to modify the parameters passed to it. 但是,我想确保传入的函数不允许修改传递给它的参数。
Here's the simple version of the function (note that T
can be, and usually is, a reference): 这是函数的简单版本(注意T
可以是,通常是一个引用):
template <class T>
void Bar(std::function<void(std::add_const_t<T>)> func)
{
// ...
}
Bad usage: 用法不好:
Bar<int&>([](int&) { /* Do nasty stuff! */ }); /* A-OK! */
I want to disallow that usage, but this code compiles perfectly fine, even though I feel like it shouldn't. 我想禁止这种用法,但是这段代码编写得非常好,即使我觉得它不应该。
Interesting thing is, if I get rid of the template parameter, ie: 有趣的是,如果我摆脱模板参数,即:
void Bar(std::function<void(const int&)> func)
{
// ...
}
Then, this usage wouldn't compile (as it shouldn't): 然后,这种用法不会编译(因为它不应该):
Bar([](int&) { /* Do nasty stuff! */ }); /* Error C2664 */
How can I enforce this and still keep the template parameter? 如何强制执行此操作并仍然保留模板参数?
Note that std::add_const_t<int &>
is int &
, for you are not adding const
to int
. 请注意, std::add_const_t<int &>
是int &
,因为您没有将const
添加到int
。 Instead you are adding const
to a reference to int
and you obtain a const reference to int
(that is int &
), not a reference to a const int
. 相反,你要添加const
的参考int
,你获得一个const引用int
(即int &
而不是一个参考const int
。
A simple way to work around it can be: 解决它的一种简单方法可以是:
#include<functional>
#include<type_traits>
template<typename T>
struct to_const_ { using type = std::add_const_t<T>; };
template<typename T>
struct to_const_<T &> { using type = std::add_const_t<T> &; };
template<typename T>
using to_const_t = typename to_const_<T>::type;
template <class T>
void Bar(std::function<void(to_const_t<T>)> func)
{}
int main() {
Bar<int&>([](int&) {});
}
The code above doesn't compile (as requested), unless you turn it to: 上面的代码不会编译(按要求),除非你把它变成:
Bar<int&>([](const int &) {});
Note that it works correctly only with lvalue references as it stands, but adding the support for rvalue references and pointers is straightforward if you got the idea. 请注意,它仅适用于左值引用,但如果您有了这个想法,则添加对右值引用和指针的支持非常简单。
It follows a minimal, (probably) working example: 它遵循一个最小的(可能)工作示例:
#include<functional>
#include<type_traits>
template<typename T>
struct to_const_ { using type = std::add_const_t<T>; };
template<typename T>
struct to_const_<T &> { using type = std::add_const_t<T> &; };
template<typename T>
struct to_const_<T &&> { using type = std::add_const_t<T> &&; };
template<typename T>
struct to_const_<T * const> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type * const, std::add_const_t<typename to_const_<T>::type> * const>; };
template<typename T>
struct to_const_<T *> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type *, std::add_const_t<typename to_const_<T>::type> *>; };
template<typename T>
using to_const_t = typename to_const_<T>::type;
template <class T>
void Bar(std::function<void(to_const_t<T>)> func)
{}
int main() {
Bar<int **>([](const int **) {});
}
You can use static_assert
to make compilation fail, with help from templates. 在模板的帮助下,您可以使用static_assert
使编译失败。
//Base case - T is non-const, possibly pointer or reference
template <typename T> struct is_const_param { static constexpr bool value = !std::is_lvalue_reference<T>::value && !std::is_rvalue_reference<T>::value && !std::is_pointer<T>::value; };
//T is const, but possibly pointer to non-const
template <typename T> struct is_const_param<const T> { static constexpr bool value = !std::is_pointer<T>::value; };
//Remove reference, try again
template <typename T> struct is_const_param<const T&> : is_const_param<const T> { };
//Remove reference, try again
template <typename T> struct is_const_param<const T&&> : is_const_param<const T> { };
//Remove pointer, try again
template <typename T> struct is_const_param<const T*> : is_const_param<const T> { };
//Remove pointer, try again
template <typename T> struct is_const_param<const T* const> : is_const_param<const T> { };
The static_assert
would just look like: static_assert
看起来像:
template <class T>
void Bar(std::function<void(T)>)
{
static_assert(is_const_param<T>::value, "...");
}
This is set up to only succeed when T
is: 设置为仅在T
为以下时才成功:
You can see some test cases here if you want to see examples of what makes it succeed or fail. 如果您想查看成功或失败的示例,可以在此处查看一些测试用例。
This was set up to allow passing by value normally, like Bar<int>
. 这被设置为允许正常传递值,如Bar<int>
。 If you want to change it to only allow Bar<const int>
, remove the is_const_param<const T*>
specialization and change the initialization of value
in the unspecialized template to false
. 如果要将其更改为仅允许Bar<const int>
,请删除is_const_param<const T*>
化并将非特定模板中value
的初始化更改为false
。 You can see that here . 你可以看到在这里 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.