简体   繁体   English

将lambda转换为std :: function时强制执行const正确性

[英]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为以下时才成功:

  1. Passed by value (see below). 通过价值传递(见下文)。
  2. Passed by constant reference. 通过不断的参考传递。
  3. Passed by pointer(s) to constant. 通过指针传递给常量。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM