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

时间:2017-03-14 23:58:10

标签: c++ lambda const

我有一个函数,它接受std::function作为参数。但是,我想确保传入的函数不允许修改传递给它的参数。

这是函数的简单版本(注意T可以是,通常是参考):

template <class T>
void Bar(std::function<void(std::add_const_t<T>)> func)
{
    // ...
}

不良用法:

Bar<int&>([](int&) { /* Do nasty stuff! */ }); /* A-OK! */

我想禁止这种用法,但是这段代码编译完全正常,即使我觉得它不应该。

有趣的是,如果我摆脱了模板参数,即:

void Bar(std::function<void(const int&)> func)
{
    // ...
}

然后,这种用法不会编译(因为它不应该):

Bar([](int&) { /* Do nasty stuff! */ }); /* Error C2664 */

如何强制执行此操作并仍然保留模板参数?

2 个答案:

答案 0 :(得分:5)

请注意,std::add_const_t<int &>int &,因为您未将const添加到int。相反,您要将const添加到对int的引用中,并获得对int(即int &)的const引用,而不是对const int的引用

解决它的一个简单方法可以是:

#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&) {});
}

上述代码无法编译(按要求),除非您将其转换为:

Bar<int&>([](const int &) {});

请注意,它只适用于左值引用,但如果您有了这个想法,则添加对右值引用和指针的支持很简单。

它遵循一个最小的(可能)工作示例:

#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 **) {});
}

答案 1 :(得分:0)

在模板的帮助下,您可以使用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> { };

static_assert看起来像是:

template <class T>
void Bar(std::function<void(T)>)
{
    static_assert(is_const_param<T>::value, "...");
}

此设置仅在T为:

时才会成功
  1. 通过价值传递(见下文)。
  2. 通过常量参考传递。
  3. 通过指针传递给常量。
  4. 如果您想查看成功或失败的示例,可以查看一些测试用例here

    这被设置为允许正常传递值,例如Bar<int>。如果您要将其更改为仅允许Bar<const int>,请删除is_const_param<const T*>专精,并将非专业化模板中value的初始化更改为false。您可以看到here